KiCad PCB EDA Suite
Loading...
Searching...
No Matches
api_handler_libraries.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 * @author Jon Evans <[email protected]>
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
22
23#include <magic_enum.hpp>
24#include <ranges>
25
26#include <api/api_enums.h>
27#include <api/api_utils.h>
29#include <kiway.h>
30#include <pgm_base.h>
32
33#include <api/common/types/base_types.pb.h>
34#include <api/common/commands/editor_commands.pb.h>
35#include <api/common/types/library_types.pb.h>
36
38
39using namespace kiapi::common::commands;
40using kiapi::common::types::LibraryCommandStatus;
41using kiapi::common::types::LibraryLoadStatus;
42using kiapi::common::types::LibraryTableScope;
43using kiapi::common::types::LibraryType;
44
46 m_type( aType )
47{
53
54 // Only the design-block instance registers LoadAllLibraries: it is the dispatcher that
55 // lazily loads the schematic and PCB kifaces and forwards to their per-type loads. If
56 // the per-type (SYMBOL/FOOTPRINT) handlers registered it too, the server's handler set
57 // (ordered by pointer, not registration order) could route the command to an instance
58 // without a KIWAY, which would fail the request.
62}
63
64
66
67
72
73
75 const wxString& aNickname ) const
76{
77 return static_cast<DESIGN_BLOCK_LIBRARY_ADAPTER&>( aAdapter ).GetDesignBlockNames( aNickname );
78}
79
80
83{
85 {
86 ApiResponseStatus e;
87 e.set_status( ApiStatusCode::AS_UNHANDLED );
88 return tl::unexpected( e );
89 }
90
93
94 if( !adapter )
95 {
96 ApiResponseStatus e;
97 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
98 e.set_error_message( "no library adapter is available for this library type" );
99 return tl::unexpected( e );
100 }
101
102 std::vector<wxString> nicknames;
103
104 if( aCtx.Request.nickname_size() > 0 )
105 {
106 for( const std::string& nickname : aCtx.Request.nickname() )
107 {
108 wxString nicknameStr = wxString::FromUTF8( nickname );
109
110 // TODO(JE) decide whether or not to do blocking loads for explicit requests
111 adapter->LoadLibraryEntry( nicknameStr );
112
113 nicknames.emplace_back( nicknameStr );
114 }
115 }
116 else
117 {
118 for( const LIBRARY_TABLE_ROW* row : adapter->Rows() )
119 nicknames.emplace_back( row->Nickname() );
120 }
121
122 LibraryItemsResponse response;
123
124 for( const wxString& nickname : nicknames )
125 {
126 for( const wxString& itemName : getItemNames( *adapter, nickname ) )
127 {
128 kiapi::common::types::LibraryIdentifier* id = response.add_items();
129 id->set_library_nickname( nickname.ToUTF8() );
130 id->set_entry_name( itemName.ToUTF8() );
131 }
132 }
133
134 return response;
135}
136
137
138static void packTableEntry( kiapi::common::types::LibraryTableEntry& aEntry, LIBRARY_TABLE_TYPE aTableType,
139 const LIBRARY_TABLE_ROW& aRow )
140{
141 aEntry.set_nickname( aRow.Nickname().ToUTF8() );
142 aEntry.set_uri( aRow.URI().ToUTF8() );
143 aEntry.set_type( ToProtoEnum<LIBRARY_TABLE_TYPE, LibraryType>( aTableType ) );
145 aEntry.set_description( aRow.Description().ToUTF8() );
146
147 for( const auto& [key, value] : aRow.GetOptionsMap() )
148 ( *aEntry.mutable_options() )[key] = value;
149
150 if( std::optional<LIBRARY_MANAGER_ADAPTER*> adapter = Pgm().GetLibraryManager().Adapter( aTableType ) )
151 aEntry.set_writable( ( *adapter )->IsWritable( aRow.Nickname() ) );
152
153 aEntry.set_disabled( aRow.Disabled() );
154 aEntry.set_hidden( aRow.Hidden() );
155}
156
157
158static void packLibraryStatusEntry( kiapi::common::types::LibraryStatusEntry* aEntry, LIBRARY_TABLE_TYPE aTableType,
159 const LIBRARY_TABLE_ROW& aRow, const LIB_STATUS& aStatus )
160{
161 packTableEntry( *aEntry->mutable_entry(), aTableType, aRow );
162 aEntry->set_status( ToProtoEnum<LOAD_STATUS, LibraryLoadStatus>( aStatus.load_status ) );
163
164 if( aStatus.error )
165 aEntry->set_error_message( aStatus.error->message.ToUTF8() );
166}
167
168
171{
173
175 {
176 ApiResponseStatus e;
177 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
178 e.set_error_message( "unknown library table scope" );
179 return tl::unexpected( e );
180 }
181
183 LibraryStatusResponse response;
184
186
187 if( !aCtx.Request.types().empty() )
188 {
189 types.clear();
190
191 for( int typeProto : aCtx.Request.types() )
192 {
193 LIBRARY_TABLE_TYPE type = FromProtoEnum<LIBRARY_TABLE_TYPE>( static_cast<LibraryType>( typeProto ) );
194
196 {
197 ApiResponseStatus e;
198 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
199 e.set_error_message( wxString::Format( "invalid library table type %d", typeProto ).ToUTF8() );
200 return tl::unexpected( e );
201 }
202
203 types.push_back( type );
204 }
205 }
206
207 for( LIBRARY_TABLE_TYPE tableType : types )
208 {
209 LIBRARY_MANAGER_ADAPTER* adapter = nullptr;
210
211 if( std::optional<LIBRARY_MANAGER_ADAPTER*> optAdapter = manager.Adapter( tableType ) )
212 adapter = *optAdapter;
213
214 for( const LIBRARY_TABLE_ROW* row : manager.Rows( tableType, scope ) )
215 {
216 if( row->Disabled() )
217 continue;
218
219 kiapi::common::types::LibraryStatusEntry* entry = response.add_libraries();
220
221 if( adapter )
222 {
223 if( std::optional<LIB_STATUS> status = adapter->GetLibraryStatus( row->Nickname() ) )
224 {
225 packLibraryStatusEntry( entry, tableType, *row, *status );
226 continue;
227 }
228 }
229
230 packLibraryStatusEntry( entry, tableType, *row, LIB_STATUS{} );
231 }
232 }
233
234 return response;
235}
236
237
240{
241 auto makeStatus =
242 []( LibraryCommandStatus::Code aCode, const wxString& aMessage = wxEmptyString )
243 {
244 LibraryCommandStatus status;
245 status.set_code( aCode );
246
247 if( !aMessage.empty() )
248 status.set_error_message( aMessage.ToUTF8() );
249
250 return status;
251 };
252
253 if( aCtx.Request.type() == LibraryType::LT_UNKNOWN )
254 {
255 ApiResponseStatus e;
256 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
257 e.set_error_message( "a valid library type is required" );
258 return tl::unexpected( e );
259 }
260
261 if( aCtx.Request.scope() == LibraryTableScope::LTS_BOTH || aCtx.Request.scope() == LibraryTableScope::LTS_UNKNOWN )
262 {
263 ApiResponseStatus e;
264 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
265 e.set_error_message( "LTS_BOTH is invalid for ReloadLibrary; choose one table" );
266 return tl::unexpected( e );
267 }
268
270 const LIBRARY_TABLE_SCOPE scope = ( aCtx.Request.scope() == LibraryTableScope::LTS_GLOBAL )
273
275
276 if( !manager.Adapter( tableType ) )
277 {
278 ApiResponseStatus e;
279 e.set_status( ApiStatusCode::AS_UNHANDLED );
280 return tl::unexpected( e );
281 }
282
283 std::vector<wxString> nicknames;
284
285 if( aCtx.Request.nickname_size() > 0 )
286 {
287 for( const std::string& nickname : aCtx.Request.nickname() )
288 nicknames.emplace_back( wxString::FromUTF8( nickname ) );
289 }
290 else
291 {
292 for( const LIBRARY_TABLE_ROW* row : manager.Rows( tableType, scope ) )
293 {
294 if( !row->Disabled() )
295 nicknames.emplace_back( row->Nickname() );
296 }
297 }
298
299 if( nicknames.empty() )
300 return makeStatus( LibraryCommandStatus::LCS_NOT_FOUND, "No libraries found to reload" );
301
302 for( const wxString& nickname : nicknames )
303 {
304 if( !manager.GetRow( tableType, nickname, scope ) )
305 {
306 return makeStatus( LibraryCommandStatus::LCS_NOT_FOUND,
307 wxString::Format( "Library '%s' not found", nickname ) );
308 }
309 }
310
311 for( const wxString& nickname : nicknames )
312 manager.ReloadLibraryEntry( tableType, nickname, scope );
313
314 return makeStatus( LibraryCommandStatus::LCS_OK );
315}
316
317
320{
321 auto makeStatus = []( LibraryCommandStatus::Code aCode, const wxString& aMessage = wxEmptyString )
322 {
323 LibraryCommandStatus status;
324 status.set_code( aCode );
325
326 if( !aMessage.empty() )
327 status.set_error_message( aMessage.ToUTF8() );
328
329 return status;
330 };
331
332 if( Pgm().IsGUI() )
333 {
334 ApiResponseStatus e;
335 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
336 e.set_error_message( "LoadAllLibraries is not available in GUI mode" );
337 return tl::unexpected( e );
338 }
339
340 std::map<LIBRARY_TABLE_TYPE, KIWAY::FACE_T> typesToLoad;
341
342 if( aCtx.Request.type_size() == 0 )
343 {
344 typesToLoad = { { LIBRARY_TABLE_TYPE::SYMBOL, KIWAY::FACE_SCH },
347 }
348 else
349 {
350 for( int protoRaw : aCtx.Request.type() )
351 {
352 LibraryType protoType = static_cast<LibraryType>( protoRaw );
353 LIBRARY_TABLE_TYPE type =
354 FromProtoEnum<LIBRARY_TABLE_TYPE, LibraryType>( static_cast<LibraryType>( protoType ) );
355
356 switch( type )
357 {
358 case LIBRARY_TABLE_TYPE::SYMBOL: typesToLoad.emplace( type, KIWAY::FACE_SCH ); break;
359 case LIBRARY_TABLE_TYPE::FOOTPRINT: typesToLoad.emplace( type, KIWAY::FACE_PCB ); break;
360 case LIBRARY_TABLE_TYPE::DESIGN_BLOCK: typesToLoad.emplace( type, KIWAY::KIWAY_FACE_COUNT ); break;
361
362 default:
363 {
364 ApiResponseStatus e;
365 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
366 e.set_error_message(
367 wxString::Format( "invalid library type %s", magic_enum::enum_name( protoType ) ).ToUTF8() );
368 return tl::unexpected( e );
369 }
370 }
371 }
372 }
373
374 // should always be set in this path
375 if( !m_kiway )
376 {
377 ApiResponseStatus e;
378 e.set_status( ApiStatusCode::AS_NOT_READY );
379 e.set_error_message( "internal error while attempting to load all libraries" );
380 return tl::unexpected( e );
381 }
382
383 KIWAY& kiway = *m_kiway;
384
385 for( const auto& [type, face] : typesToLoad )
386 {
388 {
390 continue;
391 }
392
393 KIFACE* kiface = kiway.KiFACE( face );
394
395 wxCHECK2( kiface, continue );
396
399
400 kiface->LoadAllLibraries();
401 }
402
403 return makeStatus( LibraryCommandStatus::LCS_OK );
404}
405
406
408{
409 // This base implementation handles design blocks
410 LIBRARY_MANAGER_ADAPTER* adapter = adapterForProject( Pgm().GetSettingsManager().Prj() );
411
412 LibraryCommandStatus status;
413
414 if( !adapter )
415 return status;
416
417 adapter->AsyncLoad();
418 status.set_code( LibraryCommandStatus::LCS_OK );
419 return status;
420}
421
422
425{
427 {
428 ApiResponseStatus e;
429 e.set_status( ApiStatusCode::AS_UNHANDLED );
430 return tl::unexpected( e );
431 }
432
433 wxString projectPath = wxString::FromUTF8( aCtx.Request.document().project().path() );
435 PROJECT* project = projectPath.IsEmpty() ? &mgr.Prj() : mgr.GetProjectForPath( projectPath );
436
437 if( !project )
438 {
439 ApiResponseStatus e;
440 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
441 e.set_error_message( "the requested project is not open" );
442 return tl::unexpected( e );
443 }
444
446
447 if( !adapter )
448 {
449 ApiResponseStatus e;
450 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
451 e.set_error_message( "no library adapter is available for this library type" );
452 return tl::unexpected( e );
453 }
454
455 GetItemsResponse response;
456 response.mutable_header()->mutable_document()->CopyFrom( aCtx.Request.document() );
457 google::protobuf::Any any;
458
459 if( aCtx.Request.item_ids().empty() )
460 {
461 ApiResponseStatus e;
462 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
463 e.set_error_message( "specify at least one lib_id to retrieve" );
464 return tl::unexpected( e );
465 }
466
467 std::map<wxString, std::vector<wxString>> byNickname;
468
469 for( const kiapi::common::types::LibraryIdentifier& id : aCtx.Request.item_ids() )
470 {
471 byNickname[wxString::FromUTF8( id.library_nickname() )].emplace_back( wxString::FromUTF8( id.entry_name() ) );
472 }
473
474 for( auto& [nickname, entryNames] : byNickname )
475 {
476 // TODO(JE) decide whether or not to do blocking loads for explicit requests
477 adapter->LoadLibraryEntry( nickname );
478
479 for( const wxString& entryName : entryNames )
480 {
481 if( packLibraryItem( LIB_ID( nickname, entryName ), any ) )
482 {
483 any.Swap( response.add_items() );
484 any.Clear();
485 }
486 }
487 }
488
489 if( response.items().empty() )
490 {
491 ApiResponseStatus e;
492 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
493 e.set_error_message( "none of the requested items were found" );
494 return tl::unexpected( e );
495 }
496
497 response.set_status( kiapi::common::types::ItemRequestStatus::IRS_OK );
498 return response;
499}
KICOMMON_API types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICOMMON_API KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:55
tl::expected< T, ApiResponseStatus > HANDLER_RESULT
Definition api_handler.h:47
static void packLibraryStatusEntry(kiapi::common::types::LibraryStatusEntry *aEntry, LIBRARY_TABLE_TYPE aTableType, const LIBRARY_TABLE_ROW &aRow, const LIB_STATUS &aStatus)
static void packTableEntry(kiapi::common::types::LibraryTableEntry &aEntry, LIBRARY_TABLE_TYPE aTableType, const LIBRARY_TABLE_ROW &aRow)
HANDLER_RESULT< kiapi::common::commands::LibraryItemsResponse > handleGetLibraryItems(const HANDLER_CONTEXT< kiapi::common::commands::GetLibraryItems > &aCtx)
HANDLER_RESULT< kiapi::common::commands::LibraryStatusResponse > handleGetLibraryStatuses(const HANDLER_CONTEXT< kiapi::common::commands::GetLibraryStatuses > &aCtx)
virtual bool packLibraryItem(const LIB_ID &aId, google::protobuf::Any &aOutput) const
~API_HANDLER_LIBRARIES() override
HANDLER_RESULT< kiapi::common::types::LibraryCommandStatus > handleReloadLibrary(const HANDLER_CONTEXT< kiapi::common::commands::ReloadLibrary > &aCtx)
kiapi::common::types::LibraryCommandStatus loadAllLibraries()
Starts a background load of all libraries of this handler's type.
virtual std::vector< wxString > getItemNames(LIBRARY_MANAGER_ADAPTER &aAdapter, const wxString &aNickname) const
Returns the names of all entries in the given library, or an empty vector if the library is not loade...
virtual LIBRARY_MANAGER_ADAPTER * adapterForProject(PROJECT &aProject) const
API_HANDLER_LIBRARIES(LIBRARY_TABLE_TYPE aType=LIBRARY_TABLE_TYPE::DESIGN_BLOCK)
std::function< void(KIFACE *)> m_handlerRegisterCallback
Invoked with a lazily-loaded KIFACE so its handlers can be registered on the server.
KIWAY * m_kiway
Used to lazily load kifaces for LoadAllLibraries in headless; null in GUI mode.
HANDLER_RESULT< kiapi::common::types::LibraryCommandStatus > handleLoadAllLibraries(const HANDLER_CONTEXT< kiapi::common::commands::LoadAllLibraries > &aCtx)
HANDLER_RESULT< kiapi::common::commands::GetItemsResponse > handleGetItemsFromLibrary(const HANDLER_CONTEXT< kiapi::common::commands::GetItemsFromLibrary > &aCtx)
void registerHandler(HANDLER_RESULT< ResponseType >(HandlerType::*aHandler)(const HANDLER_CONTEXT< RequestType > &))
Registers an API command handler for the given message types.
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:348
virtual KIFACE * KiFACE(FACE_T aFaceId, bool doLoad=true)
Return the KIFACE* given a FACE_T.
Definition kiway.cpp:207
@ KIWAY_FACE_COUNT
Definition kiway.h:363
@ FACE_SCH
eeschema DSO
Definition kiway.h:355
@ FACE_PCB
pcbnew DSO
Definition kiway.h:356
The interface used by the classes that actually can load IO plugins for the different parts of KiCad ...
std::optional< LIB_STATUS > GetLibraryStatus(const wxString &aNickname) const
Returns the status of a loaded library, or nullopt if the library hasn't been loaded (yet)
std::vector< LIBRARY_TABLE_ROW * > Rows(LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH, bool aIncludeInvalid=false) const
Like LIBRARY_MANAGER::Rows but filtered to the LIBRARY_TABLE_TYPE of this adapter.
void AsyncLoad()
Loads all available libraries for this adapter type in the background.
std::optional< LIB_STATUS > LoadLibraryEntry(const wxString &aNickname)
Synchronously loads the named library to LOADED state.
std::optional< LIBRARY_MANAGER_ADAPTER * > Adapter(LIBRARY_TABLE_TYPE aType) const
void ReloadLibraryEntry(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH)
Synchronously recreates the plugin for and reloads the given library.
std::vector< LIBRARY_TABLE_ROW * > Rows(LIBRARY_TABLE_TYPE aType, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH, bool aIncludeInvalid=false) const
Returns a flattened list of libraries of the given type.
std::optional< LIBRARY_TABLE_ROW * > GetRow(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH)
LIBRARY_TABLE_SCOPE Scope() const
bool Disabled() const
std::map< std::string, UTF8 > GetOptionsMap() const
const wxString & Description() const
bool Hidden() const
const wxString & URI() const
const wxString & Nickname() const
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:123
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:125
Container for project specific data.
Definition project.h:63
virtual DESIGN_BLOCK_LIBRARY_ADAPTER * DesignBlockLibs()
Return the table of design block libraries.
Definition project.cpp:433
PROJECT * GetProjectForPath(const wxString &aProjectPath) const
Return the active project iff its path matches aProjectPath, else nullptr.
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
A type-safe container of any type.
Definition ki_any.h:92
PROJECT & Prj()
Definition kicad.cpp:733
LIBRARY_TABLE_TYPE
LIBRARY_TABLE_SCOPE
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
RequestMessageType Request
Definition api_handler.h:54
Implement a participant in the KIWAY alchemy.
Definition kiway.h:153
The overall status of a loaded or loading library.
std::optional< LIBRARY_ERROR > error
LOAD_STATUS load_status
IFACE KIFACE_BASE kiface("pcb_test_frame", KIWAY::FACE_PCB)