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 {
313 manager.ReloadLibraryEntry( tableType, nickname, scope );
314 // Sync reload
315 manager.LoadLibraryEntry( tableType, nickname );
316 }
317
318 return makeStatus( LibraryCommandStatus::LCS_OK );
319}
320
321
324{
325 auto makeStatus = []( LibraryCommandStatus::Code aCode, const wxString& aMessage = wxEmptyString )
326 {
327 LibraryCommandStatus status;
328 status.set_code( aCode );
329
330 if( !aMessage.empty() )
331 status.set_error_message( aMessage.ToUTF8() );
332
333 return status;
334 };
335
336 if( Pgm().IsGUI() )
337 {
338 ApiResponseStatus e;
339 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
340 e.set_error_message( "LoadAllLibraries is not available in GUI mode" );
341 return tl::unexpected( e );
342 }
343
344 std::map<LIBRARY_TABLE_TYPE, KIWAY::FACE_T> typesToLoad;
345
346 if( aCtx.Request.type_size() == 0 )
347 {
348 typesToLoad = { { LIBRARY_TABLE_TYPE::SYMBOL, KIWAY::FACE_SCH },
351 }
352 else
353 {
354 for( int protoRaw : aCtx.Request.type() )
355 {
356 LibraryType protoType = static_cast<LibraryType>( protoRaw );
357 LIBRARY_TABLE_TYPE type =
358 FromProtoEnum<LIBRARY_TABLE_TYPE, LibraryType>( static_cast<LibraryType>( protoType ) );
359
360 switch( type )
361 {
362 case LIBRARY_TABLE_TYPE::SYMBOL: typesToLoad.emplace( type, KIWAY::FACE_SCH ); break;
363 case LIBRARY_TABLE_TYPE::FOOTPRINT: typesToLoad.emplace( type, KIWAY::FACE_PCB ); break;
364 case LIBRARY_TABLE_TYPE::DESIGN_BLOCK: typesToLoad.emplace( type, KIWAY::KIWAY_FACE_COUNT ); break;
365
366 default:
367 {
368 ApiResponseStatus e;
369 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
370 e.set_error_message(
371 wxString::Format( "invalid library type %s", magic_enum::enum_name( protoType ) ).ToUTF8() );
372 return tl::unexpected( e );
373 }
374 }
375 }
376 }
377
378 // should always be set in this path
379 if( !m_kiway )
380 {
381 ApiResponseStatus e;
382 e.set_status( ApiStatusCode::AS_NOT_READY );
383 e.set_error_message( "internal error while attempting to load all libraries" );
384 return tl::unexpected( e );
385 }
386
387 KIWAY& kiway = *m_kiway;
388
389 for( const auto& [type, face] : typesToLoad )
390 {
392 {
394 continue;
395 }
396
397 KIFACE* kiface = kiway.KiFACE( face );
398
399 wxCHECK2( kiface, continue );
400
403
404 kiface->LoadAllLibraries();
405 }
406
407 return makeStatus( LibraryCommandStatus::LCS_OK );
408}
409
410
412{
413 // This base implementation handles design blocks
414 LIBRARY_MANAGER_ADAPTER* adapter = adapterForProject( Pgm().GetSettingsManager().Prj() );
415
416 LibraryCommandStatus status;
417
418 if( !adapter )
419 return status;
420
421 adapter->AsyncLoad();
422 status.set_code( LibraryCommandStatus::LCS_OK );
423 return status;
424}
425
426
429{
431 {
432 ApiResponseStatus e;
433 e.set_status( ApiStatusCode::AS_UNHANDLED );
434 return tl::unexpected( e );
435 }
436
437 wxString projectPath = wxString::FromUTF8( aCtx.Request.document().project().path() );
439 PROJECT* project = projectPath.IsEmpty() ? &mgr.Prj() : mgr.GetProjectForPath( projectPath );
440
441 if( !project )
442 {
443 ApiResponseStatus e;
444 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
445 e.set_error_message( "the requested project is not open" );
446 return tl::unexpected( e );
447 }
448
450
451 if( !adapter )
452 {
453 ApiResponseStatus e;
454 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
455 e.set_error_message( "no library adapter is available for this library type" );
456 return tl::unexpected( e );
457 }
458
459 GetItemsResponse response;
460 response.mutable_header()->mutable_document()->CopyFrom( aCtx.Request.document() );
461 google::protobuf::Any any;
462
463 if( aCtx.Request.item_ids().empty() )
464 {
465 ApiResponseStatus e;
466 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
467 e.set_error_message( "specify at least one lib_id to retrieve" );
468 return tl::unexpected( e );
469 }
470
471 std::map<wxString, std::vector<wxString>> byNickname;
472
473 for( const kiapi::common::types::LibraryIdentifier& id : aCtx.Request.item_ids() )
474 {
475 byNickname[wxString::FromUTF8( id.library_nickname() )].emplace_back( wxString::FromUTF8( id.entry_name() ) );
476 }
477
478 for( auto& [nickname, entryNames] : byNickname )
479 {
480 // TODO(JE) decide whether or not to do blocking loads for explicit requests
481 adapter->LoadLibraryEntry( nickname );
482
483 for( const wxString& entryName : entryNames )
484 {
485 if( packLibraryItem( LIB_ID( nickname, entryName ), any ) )
486 {
487 any.Swap( response.add_items() );
488 any.Clear();
489 }
490 }
491 }
492
493 if( response.items().empty() )
494 {
495 ApiResponseStatus e;
496 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
497 e.set_error_message( "none of the requested items were found" );
498 return tl::unexpected( e );
499 }
500
501 response.set_status( kiapi::common::types::ItemRequestStatus::IRS_OK );
502 return response;
503}
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:45
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.
Definition api_handler.h:93
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:340
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:355
@ FACE_SCH
eeschema DSO
Definition kiway.h:347
@ FACE_PCB
pcbnew DSO
Definition kiway.h:348
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)
std::optional< LIB_STATUS > LoadLibraryEntry(LIBRARY_TABLE_TYPE aType, const wxString &aNickname)
Synchronously loads the named library to LOADED state for the given type.
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:727
LIBRARY_TABLE_TYPE
LIBRARY_TABLE_SCOPE
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
RequestMessageType Request
Definition api_handler.h:52
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)