KiCad PCB EDA Suite
Loading...
Searching...
No Matches
symbol_library_adapter.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
21#include <magic_enum.hpp>
22#include <chrono>
23#include <memory>
24
25#include <common.h>
26#include <dialog_shim.h>
27#include <ki_exception.h>
28#include <wx/log.h>
29
30#include <lib_symbol.h>
32#include <sch_io/sch_io_mgr.h>
33
34#include <env_vars.h>
35#include <pgm_base.h>
36#include <project.h>
37#include <thread_pool.h>
38#include <trace_helpers.h>
40
41using namespace std::chrono_literals;
42
43
44const char* SYMBOL_LIBRARY_ADAPTER::PropPowerSymsOnly = "pwr_sym_only";
45const char* SYMBOL_LIBRARY_ADAPTER::PropNonPowerSymsOnly = "non_pwr_sym_only";
46
48
50
51
56
57
63
64
69
70
72{
73 SCH_IO* ret = dynamic_cast<SCH_IO*>( aRow->plugin.get() );
74 wxCHECK( aRow->plugin && ret, nullptr );
75 return ret;
76}
77
78
79void SYMBOL_LIBRARY_ADAPTER::enumerateLibrary( LIB_DATA* aLib, const wxString& aUri )
80{
81 wxArrayString dummyList;
82
83 std::lock_guard pluginGuard( pluginMutex( aLib->row->Nickname() ) );
84
85 std::map<std::string, UTF8> options = aLib->row->GetOptionsMap();
86 schplugin( aLib )->EnumerateSymbolLib( dummyList, aUri, &options );
87}
88
89
90std::optional<LIB_STATUS> SYMBOL_LIBRARY_ADAPTER::LoadOne( LIB_DATA* aLib )
91{
93
94 std::lock_guard pluginGuard( pluginMutex( aLib->row->Nickname() ) );
95
96 std::map<std::string, UTF8> options = aLib->row->GetOptionsMap();
97
98 try
99 {
100 wxArrayString dummyList;
101 schplugin( aLib )->EnumerateSymbolLib( dummyList, getUri( aLib->row ), &options );
102 wxLogTrace( traceLibraries, "Sym: %s: library enumerated %zu items", aLib->row->Nickname(), dummyList.size() );
104 aLib->status.error.reset();
105 }
106 catch( IO_ERROR& e )
107 {
109 aLib->status.error = LIBRARY_ERROR( e.Problem(), e.Where() );
110 wxLogTrace( traceLibraries, "Sym: %s: plugin threw exception: %s", aLib->row->Nickname(), e.What() );
111 }
112
113 return aLib->status;
114}
115
116
117std::optional<LIB_STATUS> SYMBOL_LIBRARY_ADAPTER::CheckLibrary( LIB_DATA* aLib )
118{
120
121 std::lock_guard pluginGuard( pluginMutex( aLib->row->Nickname() ) );
122
123 std::map<std::string, UTF8> options = aLib->row->GetOptionsMap();
124
125 try
126 {
127 schplugin( aLib )->CheckLibrary( getUri( aLib->row ), &options );
129 aLib->status.error.reset();
130 }
131 catch( IO_ERROR& e )
132 {
134 aLib->status.error = LIBRARY_ERROR( e.Problem(), e.Where() );
135 wxLogTrace( traceLibraries, "Sym: %s: library check failed: %s", aLib->row->Nickname(), e.What() );
136 }
137
138 return aLib->status;
139}
140
141
142std::optional<LIB_STATUS> SYMBOL_LIBRARY_ADAPTER::LoadOne( const wxString& nickname )
143{
145
146 if( result.has_value() )
147 return LoadOne( *result );
148
149 return LIB_STATUS { .load_status = LOAD_STATUS::LOAD_ERROR,
150 .error = LIBRARY_ERROR( { result.error() } ) };
151}
152
153
155{
156 SCH_IO_MGR::SCH_FILE_T type = SCH_IO_MGR::EnumFromStr( row->Type() );
157
158 if( type == SCH_IO_MGR::SCH_NESTED_TABLE )
159 {
160 wxString msg;
161 wxFileName fileName( m_manager.GetFullURI( row, true, &m_manager.Project() ) );
162
163 if( fileName.FileExists() )
164 return tl::unexpected( LIBRARY_TABLE_OK() );
165 else
166 msg = wxString::Format( _( "Nested table '%s' not found." ), row->URI() );
167
168 return tl::unexpected( LIBRARY_ERROR( msg ) );
169 }
170 else if( type == SCH_IO_MGR::SCH_FILE_UNKNOWN )
171 {
172 wxLogTrace( traceLibraries, "Sym: Plugin type %s is unknown!", row->Type() );
173 wxString msg = wxString::Format( _( "Unknown library type %s " ), row->Type() );
174 return tl::unexpected( LIBRARY_ERROR( msg ) );
175 }
176
177 SCH_IO* plugin = SCH_IO_MGR::FindPlugin( type );
178 wxCHECK( plugin, tl::unexpected( LIBRARY_ERROR( _( "Internal error" ) ) ) );
179
180 plugin->SetLibraryManagerAdapter( this );
181
182 wxLogTrace( traceLibraries, "Sym: Library %s (%s) plugin created", row->Nickname(),
183 magic_enum::enum_name( row->Scope() ) );
184
185 return plugin;
186}
187
188
189std::vector<LIB_SYMBOL*> SYMBOL_LIBRARY_ADAPTER::GetSymbols( const wxString& aNickname, SYMBOL_TYPE aType )
190{
191 std::vector<LIB_SYMBOL*> symbols;
192
193 std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname );
194
195 if( !maybeLib )
196 return symbols;
197
198 const LIB_DATA* lib = *maybeLib;
199
200 std::lock_guard pluginGuard( pluginMutex( aNickname ) );
201
202 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
203
204 if( aType == SYMBOL_TYPE::POWER_ONLY )
205 options[PropPowerSymsOnly] = "";
206
207 try
208 {
209 schplugin( lib )->EnumerateSymbolLib( symbols, getUri( lib->row ), &options );
210 }
211 catch( IO_ERROR& e )
212 {
213 wxLogTrace( traceLibraries, "Sym: Exception enumerating library %s: %s", lib->row->Nickname(), e.What() );
214 }
215
216 for( LIB_SYMBOL* symbol : symbols )
217 {
218 LIB_ID id = symbol->GetLibId();
219 id.SetLibNickname( lib->row->Nickname() );
220 symbol->SetLibId( id );
221 }
222
223 return symbols;
224}
225
226
227std::vector<wxString> SYMBOL_LIBRARY_ADAPTER::GetSymbolNames( const wxString& aNickname, SYMBOL_TYPE aType )
228{
229 wxArrayString namesAS;
230 std::vector<wxString> names;
231
232 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
233 {
234 const LIB_DATA* lib = *maybeLib;
235
236 std::lock_guard pluginGuard( pluginMutex( aNickname ) );
237
238 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
239
240 if( aType == SYMBOL_TYPE::POWER_ONLY )
241 options[PropPowerSymsOnly] = "";
242
243 try
244 {
245 schplugin( lib )->EnumerateSymbolLib( namesAS, getUri( lib->row ), &options );
246 }
247 catch( const IO_ERROR& e )
248 {
249 wxLogTrace( traceLibraries, "Sym: Exception enumerating library %s: %s",
250 lib->row->Nickname(), e.What() );
251 }
252 }
253
254 for( const wxString& name : namesAS )
255 names.emplace_back( name );
256
257 return names;
258}
259
260
261LIB_SYMBOL* SYMBOL_LIBRARY_ADAPTER::LoadSymbol( const wxString& aNickname, const wxString& aName )
262{
263 if( std::optional<const LIB_DATA*> lib = fetchIfLoaded( aNickname ) )
264 {
265 std::lock_guard pluginGuard( pluginMutex( aNickname ) );
266
267 std::map<std::string, UTF8> options = ( *lib )->row->GetOptionsMap();
268
269 if( LIB_SYMBOL* symbol = schplugin( *lib )->LoadSymbol( getUri( ( *lib )->row ), aName, &options ) )
270 {
271 LIB_ID id = symbol->GetLibId();
272 id.SetLibNickname( ( *lib )->row->Nickname() );
273 symbol->SetLibId( id );
274 return symbol;
275 }
276 }
277 else
278 {
279 wxLogTrace( traceLibraries, "LoadSymbol: requested library %s not loaded", aNickname );
280 }
281
282 return nullptr;
283}
284
285
287 std::unique_ptr<LIB_SYMBOL> aSymbol, bool aOverwrite )
288{
289 wxCHECK( aSymbol, SAVE_SKIPPED );
290
291 LIBRARY_RESULT<LIB_DATA*> libResult = loadIfNeeded( aNickname );
292
293 if( !libResult.has_value() )
294 {
295 wxLogTrace( traceLibraries, "SaveSymbol: unable to load library %s: %s", aNickname,
296 libResult.error().message );
297 return SAVE_SKIPPED;
298 }
299
300 LIB_DATA* lib = *libResult;
301
302 if( !lib )
303 {
304 wxLogTrace( traceLibraries, "SaveSymbol: library %s not found", aNickname );
305 return SAVE_SKIPPED;
306 }
307
308 SCH_IO* plugin = schplugin( lib );
309 wxCHECK( plugin, SAVE_SKIPPED );
310
311 std::lock_guard pluginGuard( pluginMutex( aNickname ) );
312
313 const wxString symbolName = aSymbol->GetName();
314
315 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
316
317 if( !aOverwrite )
318 {
319 try
320 {
321 LIB_SYMBOL* existing = plugin->LoadSymbol( getUri( lib->row ), symbolName, &options );
322
323 if( existing )
324 return SAVE_SKIPPED;
325 }
326 catch( const IO_ERROR& e )
327 {
328 wxLogTrace( traceLibraries, "SaveSymbol: error checking for existing symbol %s:%s: %s", aNickname,
329 symbolName, e.What() );
330 return SAVE_SKIPPED;
331 }
332 }
333
334 try
335 {
336 plugin->SaveSymbol( getUri( lib->row ), std::move( aSymbol ), &options );
337 }
338 catch( const IO_ERROR& e )
339 {
340 wxLogTrace( traceLibraries, "SaveSymbol: error saving %s:%s: %s", aNickname, symbolName, e.What() );
341 return SAVE_SKIPPED;
342 }
343
344 return SAVE_OK;
345}
346
347
348void SYMBOL_LIBRARY_ADAPTER::DeleteSymbol( const wxString& aNickname, const wxString& aSymbolName )
349{
350 LIBRARY_RESULT<LIB_DATA*> libResult = loadIfNeeded( aNickname );
351
352 if( !libResult.has_value() || !*libResult )
353 {
354 wxLogTrace( traceLibraries, "DeleteSymbol: unable to load library %s", aNickname );
355 return;
356 }
357
358 LIB_DATA* lib = *libResult;
359
360 std::lock_guard lock( pluginMutex( aNickname ) );
361
362 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
363 schplugin( lib )->DeleteSymbol( getUri( lib->row ), aSymbolName, &options );
364}
365
366
368{
369 // Route through fetchIfLoaded() so LOAD_ERROR sentinel entries, which carry a null
370 // plugin, are filtered out instead of dereferenced.
371 if( std::optional<const LIB_DATA*> lib = fetchIfLoaded( aLib ) )
372 return ( *lib )->plugin->IsLibraryWritable( getUri( ( *lib )->row ) );
373
374 return false;
375}
376
377
378std::vector<wxString> SYMBOL_LIBRARY_ADAPTER::GetAvailableExtraFields( const wxString& aNickname )
379{
380 std::vector<wxString> fields;
381
382 if( std::optional<LIB_DATA*> result = fetchIfLoaded( aNickname ) )
383 {
384 LIB_DATA* rowData = *result;
385
386 int hash = schplugin( rowData )->GetModifyHash();
387
388 if( hash != rowData->modify_hash )
389 {
390 rowData->modify_hash = hash;
392 }
393
394 return rowData->available_fields_cache;
395 }
396
397 return fields;
398}
399
400
401bool SYMBOL_LIBRARY_ADAPTER::SupportsSubLibraries( const wxString& aNickname ) const
402{
403 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
404 {
405 const LIB_DATA* rowData = *result;
406 return schplugin( rowData )->SupportsSubLibraries();
407 }
408
409 return false;
410}
411
412
413std::vector<SUB_LIBRARY> SYMBOL_LIBRARY_ADAPTER::GetSubLibraries( const wxString& aNickname ) const
414{
415 std::vector<SUB_LIBRARY> ret;
416
417 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
418 {
419 const LIB_DATA* rowData = *result;
420
421 try
422 {
423 std::vector<wxString> names;
424 schplugin( rowData )->GetSubLibraryNames( names );
425
426 for( const wxString& name : names )
427 {
428 ret.emplace_back( SUB_LIBRARY {
429 .nickname = name,
430 .description = schplugin( rowData )->GetSubLibraryDescription( name ) } );
431 }
432 }
433 catch( const IO_ERROR& e )
434 {
435 wxLogTrace( traceLibraries, "Sym: Exception getting sub-libraries for %s: %s",
436 aNickname, e.What() );
437 }
438 }
439
440 return ret;
441}
442
443
445{
446 // Queried on every library table repaint, so ask each plugin type only once (UI thread only)
447 static std::map<SCH_IO_MGR::SCH_FILE_T, bool> s_supported;
448
449 SCH_IO_MGR::SCH_FILE_T type = SCH_IO_MGR::EnumFromStr( aRow.Type() );
450 auto it = s_supported.find( type );
451
452 if( it == s_supported.end() )
453 {
454 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( type ) );
455 it = s_supported.emplace( type, plugin && plugin->SupportsConfigurationDialog() ).first;
456 }
457
458 return it->second;
459}
460
461
463 wxWindow* aParent )
464{
465 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::EnumFromStr( aRow.Type() ) ) );
466
467 if( !plugin || !plugin->SupportsConfigurationDialog() )
468 return wxID_CANCEL;
469
470 plugin->SetLibraryManagerAdapter( this );
471
472 // Resolved like a normal load so a relative URI and ${CWD} in the connection string agree
473 wxString path = getUri( &aRow );
474 std::unique_ptr<DIALOG_SHIM> dialog( plugin->CreateConfigurationDialog( aParent, path ) );
475
476 if( !dialog )
477 return wxID_CANCEL;
478
479 int ret = dialog->ShowModal();
480
481 if( ret == wxID_OK )
482 {
483 // An edited or new grid row is reloaded when the outer table is applied instead
484 std::optional<LIBRARY_TABLE_ROW*> committed = m_manager.GetRow( Type(), aRow.Nickname(), aScope );
485
486 if( committed && ( *committed )->URI() == aRow.URI() && ( *committed )->Type() == aRow.Type() )
487 ReloadLibraryEntry( aRow.Nickname(), aScope );
488 }
489
490 return ret;
491}
492
493
495{
496 int hash = 0;
497
498 for( const LIBRARY_TABLE_ROW* row : m_manager.Rows( Type() ) )
499 {
500 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( row->Nickname() ) )
501 {
502 const LIB_DATA* rowData = *result;
503 wxCHECK2( rowData->row, continue );
504 hash += schplugin( rowData )->GetModifyHash();
505 }
506 }
507
508 return hash;
509}
510
511
512std::optional<int> SYMBOL_LIBRARY_ADAPTER::GetLibraryModifyHash( const wxString& aNickname ) const
513{
514 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
515 {
516 const LIB_DATA* rowData = *result;
517 wxCHECK( rowData->row, std::nullopt );
518 return schplugin( rowData )->GetModifyHash();
519 }
520
521 return std::nullopt;
522}
const char * name
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
virtual const wxString Problem() const
what was the problem?
virtual const wxString Where() const
where did the Problem() occur?
A wrapper for static data that should not be destroyed at program exit.
void resetProjectCache()
Aborts pending loads and resets every project-scope cache entry in place (plugin and row cleared,...
void ReloadLibraryEntry(const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH)
Synchronously recreates the plugin for and reloads the given library.
void evictOwnedGlobalEntries()
Erases this adapter's own entries (LIB_DATA::global_owner == this) from the process-wide globalLibs()...
LIBRARY_MANAGER_ADAPTER(LIBRARY_MANAGER &aManager)
Constructs a type-specific adapter into the library manager.
wxString getUri(const LIBRARY_TABLE_ROW *aRow) const
LIBRARY_RESULT< LIB_DATA * > loadIfNeeded(const wxString &aNickname)
Fetches a loaded library, triggering a load of that library if it isn't loaded yet.
static std::recursive_mutex & pluginMutex(const wxString &aNickname)
Serializes access to a library's shared plugin instance so its single mutable cache is not raced by c...
LIBRARY_MANAGER & m_manager
std::optional< const LIB_DATA * > fetchIfLoaded(const wxString &aNickname) const
LIBRARY_TABLE_SCOPE Scope() const
std::map< std::string, UTF8 > GetOptionsMap() const
const wxString & Type() 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
int SetLibNickname(const UTF8 &aLibNickname)
Override the logical library name portion of the LIB_ID to aLibNickname.
Definition lib_id.cpp:113
Define a library symbol object.
Definition lib_symbol.h:114
static SCH_FILE_T EnumFromStr(const wxString &aFileType)
Return the file type from the corresponding plugin type name: "kicad", "legacy", etc.
Base class that schematic file and library loading and saving plugins should derive from.
Definition sch_io.h:60
virtual void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr)
Populate a list of LIB_SYMBOL alias names contained within the library aLibraryPath.
Definition sch_io.cpp:90
virtual bool SupportsSubLibraries() const
Definition sch_io.h:321
virtual int GetModifyHash() const =0
Return the modification hash from the library cache.
virtual void GetAvailableSymbolFields(std::vector< wxString > &aNames)
Retrieves a list of (custom) field names that are present on symbols in this library.
Definition sch_io.h:357
virtual void DeleteSymbol(const wxString &aLibraryPath, const wxString &aSymbolName, const std::map< std::string, UTF8 > *aProperties=nullptr)
Delete the entire LIB_SYMBOL associated with aAliasName from the library aLibraryPath.
Definition sch_io.cpp:132
virtual void CheckLibrary(const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr)
Validate that the library at aLibraryPath is reachable and well-formed, without necessarily loading s...
Definition sch_io.cpp:108
virtual LIB_SYMBOL * LoadSymbol(const wxString &aLibraryPath, const wxString &aPartName, const std::map< std::string, UTF8 > *aProperties=nullptr)
Load a LIB_SYMBOL object having aPartName from the aLibraryPath containing a library format that this...
Definition sch_io.cpp:116
virtual wxString GetSubLibraryDescription(const wxString &aName)
Gets a description of a sublibrary.
Definition sch_io.h:346
virtual void GetSubLibraryNames(std::vector< wxString > &aNames)
Retrieves a list of sub-libraries in this library.
Definition sch_io.h:335
std::optional< LIB_STATUS > LoadOne(LIB_DATA *aLib) override
Loads or reloads the given library, if it exists.
static SCH_IO * schplugin(const LIB_DATA *aRow)
LIB_SYMBOL * LoadSymbol(const wxString &aNickname, const wxString &aName)
Load a LIB_SYMBOL having aName from the library given by aNickname.
void DeleteSymbol(const wxString &aNickname, const wxString &aSymbolName)
Deletes the aSymbolName from the library given by aNickname.
std::optional< LIB_STATUS > CheckLibrary(LIB_DATA *aLib) override
Validates that a library is loadable. May not necessarily load the library!
SAVE_T SaveSymbol(const wxString &aNickname, std::unique_ptr< LIB_SYMBOL > aSymbol, bool aOverwrite=true)
Write aSymbol to an existing library given by aNickname.
void enumerateLibrary(LIB_DATA *aLib, const wxString &aUri) override
Override in derived class to perform library-specific enumeration.
std::vector< wxString > GetSymbolNames(const wxString &aNickname, SYMBOL_TYPE aType=SYMBOL_TYPE::ALL_SYMBOLS)
bool SupportsSubLibraries(const wxString &aNickname) const
LIBRARY_RESULT< IO_BASE * > createPlugin(const LIBRARY_TABLE_ROW *row) override
Creates a concrete plugin for the given row.
int ShowConfigurationDialog(const LIBRARY_TABLE_ROW &aRow, LIBRARY_TABLE_SCOPE aScope, wxWindow *aParent) override
Shows the plugin's configuration dialog for a library table row.
std::vector< SUB_LIBRARY > GetSubLibraries(const wxString &aNickname) const
SYMBOL_LIBRARY_ADAPTER(LIBRARY_MANAGER &aManager)
static wxString GlobalPathEnvVariableName()
static const char * PropPowerSymsOnly
LIBRARY_TABLE_TYPE Type() const override
The type of library table this adapter works with.
IO_BASE * plugin(const LIB_DATA *aRow) override
std::vector< LIB_SYMBOL * > GetSymbols(const wxString &aNickname, SYMBOL_TYPE aType=SYMBOL_TYPE::ALL_SYMBOLS)
static LEAK_AT_EXIT< std::shared_mutex > GlobalLibraryMutex
static LEAK_AT_EXIT< std::map< wxString, LIB_DATA > > GlobalLibraries
bool SupportsConfigurationDialog(const LIBRARY_TABLE_ROW &aRow) const override
Configuration works from the row itself, so libraries that failed to load or are not yet committed to...
std::vector< wxString > GetAvailableExtraFields(const wxString &aNickname)
Returns a list of additional (non-mandatory) symbol fields present in the given library.
SAVE_T
The set of return values from SaveSymbol() below.
static const char * PropNonPowerSymsOnly
bool IsSymbolLibWritable(const wxString &aNickname)
Return true if the library given by aNickname is writable.
std::optional< int > GetLibraryModifyHash(const wxString &aNickname) const
Return the modify hash of a single library if it is currently loaded.
#define _(s)
Functions related to environment variables, including help functions.
const wxChar *const traceLibraries
Flag to enable library table and library manager tracing.
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
tl::expected< ResultType, LIBRARY_ERROR > LIBRARY_RESULT
LIBRARY_TABLE_SCOPE
KICOMMON_API wxString GetVersionedEnvVarName(const wxString &aBaseName)
Construct a versioned environment variable based on this KiCad major version.
Definition env_vars.cpp:78
see class PGM_BASE
Storage for an actual loaded library (including library content owned by the plugin)
std::vector< wxString > available_fields_cache
LIB_STATUS status
std::unique_ptr< IO_BASE > plugin
Never destroyed while holding the map's mutex; a plugin's worker thread may be blocked on it.
const LIBRARY_TABLE_ROW * row
The overall status of a loaded or loading library.
std::optional< LIBRARY_ERROR > error
LOAD_STATUS load_status
A descriptor for a sub-library (supported by database and http libraries)
std::string path
wxString result
Test unit parsing edge cases and error handling.
wxLogTrace helper definitions.