1115 if( !aMessage.is_object() )
1118 const wxString command =
jsonString( aMessage,
"command" );
1120 if( command.IsEmpty() )
1123 auto messageIdIt = aMessage.find(
"message_id" );
1125 if( messageIdIt == aMessage.end() || !messageIdIt->is_number_integer() )
1128 const int messageId = messageIdIt->get<
int>();
1130 const int version = aMessage.value(
"version", 0 );
1135 wxString::Format(
_(
"Unsupported RPC version %d." ), version ) );
1139 const wxString sessionId =
jsonString( aMessage,
"session_id" );
1141 if( sessionId.IsEmpty() )
1144 _(
"Missing session identifier." ) );
1148 if( !sessionId.IsSameAs(
m_sessionId.AsString() ) )
1149 wxLogWarning(
"Remote symbol RPC session mismatch (expected %s, got %s).",
1152 const wxString status =
jsonString( aMessage,
"status" );
1154 if( status.IsSameAs( wxS(
"ERROR" ),
false ) )
1156 wxLogWarning(
"Remote symbol RPC error (%s): %s",
jsonString( aMessage,
"error_code" ),
1161 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"handleRpcMessage: command=%s message_id=%d status=%s session=%s",
1162 command.ToUTF8().data(), messageId, status.ToUTF8().data(), sessionId.ToUTF8().data() );
1164 nlohmann::json params = nlohmann::json::object();
1165 auto paramsIt = aMessage.find(
"parameters" );
1167 if( paramsIt != aMessage.end() && paramsIt->is_object() )
1170 const std::string data = aMessage.value(
"data", std::string() );
1172 if( command == wxS(
"NEW_SESSION" ) )
1174 nlohmann::json reply = nlohmann::json::object();
1175 reply[
"client_name"] =
"KiCad";
1181 else if( command == wxS(
"GET_KICAD_VERSION" ) )
1183 nlohmann::json reply = nlohmann::json::object();
1188 else if( command == wxS(
"LIST_SUPPORTED_VERSIONS" ) )
1190 nlohmann::json reply = nlohmann::json::object();
1195 else if( command == wxS(
"CAPABILITIES" ) )
1197 nlohmann::json reply = nlohmann::json::object();
1198 reply[
"commands"] = {
"NEW_SESSION",
"GET_KICAD_VERSION",
"LIST_SUPPORTED_VERSIONS",
1199 "CAPABILITIES",
"PING",
"PONG",
"REMOTE_LOGIN",
"LOGOUT",
"DL_SYMBOL",
"DL_COMPONENT",
"DL_FOOTPRINT",
1200 "DL_SPICE",
"DL_3DMODEL" };
1201 reply[
"compression"] = {
"NONE",
"ZSTD" };
1202 reply[
"max_message_size"] = 0;
1206 else if( command == wxS(
"PING" ) )
1208 nlohmann::json reply = nlohmann::json::object();
1210 if( params.contains(
"nonce" ) )
1211 reply[
"nonce"] = params[
"nonce"];
1216 else if( command == wxS(
"PONG" ) )
1220 else if( command == wxS(
"REMOTE_LOGIN" ) )
1225 else if( command == wxS(
"LOGOUT" ) )
1243 const wxString compression =
jsonString( params,
"compression" );
1245 if( command.StartsWith( wxS(
"DL_" ) ) )
1247 if( compression.IsEmpty() )
1249 respondWithError( command, messageId, wxS(
"INVALID_PARAMETERS" ),
_(
"Missing compression metadata." ) );
1253 std::vector<uint8_t> decoded;
1262 std::vector<uint8_t> payload;
1263 wxScopedCharBuffer compUtf8 = compression.ToUTF8();
1264 std::string compressionStr = compUtf8 ? std::string( compUtf8.data() ) : std::string();
1272 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"handleRpcMessage: decoded size=%zu decompressed size=%zu command=%s",
1273 decoded.size(), payload.size(), command.ToUTF8().data() );
1275 nlohmann::json responseParams = nlohmann::json::object();
1278 if( command == wxS(
"DL_SYMBOL" ) )
1280 else if( command == wxS(
"DL_COMPONENT" ) )
1282 else if( command == wxS(
"DL_FOOTPRINT" ) )
1284 else if( command == wxS(
"DL_3DMODEL" ) )
1286 else if( command == wxS(
"DL_SPICE" ) )
1291 wxString::Format(
_(
"Command '%s' is not supported." ), command ) );
1296 sendRpcMessage( command, std::move( responseParams ), messageId );
1299 error.IsEmpty() ?
_(
"Unable to store payload." ) : error );
1305 wxString::Format(
_(
"Command '%s' is not supported." ), command ) );
1580 const std::vector<uint8_t>& aPayload,
1583 const wxString mode =
jsonString( aParams,
"mode" );
1584 const bool placeAfterDownload = mode.IsSameAs( wxS(
"PLACE" ),
false );
1586 if( !mode.IsEmpty() && !mode.IsSameAs( wxS(
"SAVE" ),
false )
1587 && !mode.IsSameAs( wxS(
"PLACE" ),
false ) )
1589 aError = wxString::Format(
_(
"Unsupported transfer mode '%s'." ), mode );
1593 const wxString contentType =
jsonString( aParams,
"content_type" );
1595 if( !contentType.IsSameAs( wxS(
"KICAD_SYMBOL_V1" ),
false ) )
1597 aError =
_(
"Unsupported symbol payload type." );
1603 aError =
_(
"No schematic editor is available to store symbols." );
1611 aError =
_(
"Unable to load schematic settings." );
1622 wxFileName symDir = baseDir;
1623 symDir.AppendDir( wxS(
"symbols" ) );
1625 if( !symDir.DirExists() && !symDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
1627 aError = wxString::Format(
_(
"Unable to create '%s'." ), symDir.GetFullPath() );
1631 wxString libItemName =
jsonString( aParams,
"name" );
1632 wxString baseName = libItemName;
1634 if( baseName.IsEmpty() )
1637 if( baseName.IsEmpty() )
1638 baseName = wxS(
"symbol" );
1640 baseName.Trim(
true ).Trim(
false );
1642 if( libItemName.IsEmpty() )
1643 libItemName = baseName;
1647 if( libItemName.IsEmpty() )
1648 libItemName = sanitizedName;
1650 const wxString nickname =
sanitizedPrefix() + wxS(
"_sym_" ) + sanitizedName;
1652 wxFileName outFile( symDir );
1653 outFile.SetFullName( nickname + wxS(
".kicad_sym" ) );
1662 aError =
_(
"Unable to access the symbol library manager." );
1667 std::optional<LIB_STATUS> libStatus = adapter->
LoadOne( nickname );
1668 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
1669 "receiveSymbol: LoadOne(%s) returned %s",
1670 nickname.ToUTF8().data(),
1671 libStatus.has_value() ?
"valid status" :
"nullopt" );
1673 std::unique_ptr<LIB_SYMBOL> downloadedSymbol =
loadSymbolFromPayload( aPayload, libItemName, aError );
1675 if( !downloadedSymbol )
1677 if( aError.IsEmpty() )
1678 aError =
_(
"Unable to parse the downloaded symbol." );
1683 downloadedSymbol->SetName( libItemName );
1688 downloadedSymbol->SetLibId( savedId );
1691 std::string newChecksum = HashBuffer( aPayload );
1692 std::string existingChecksum;
1694 if( ComputeSymbolChecksum(
nullptr, outFile, libItemName, existingChecksum )
1695 && existingChecksum == newChecksum )
1697 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
1698 "receiveSymbol: symbol %s already exists with same content, skipping save",
1699 libItemName.ToUTF8().data() );
1702 if( placeAfterDownload )
1704 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
1705 "receiveSymbol: placing existing symbol (nickname=%s libItem=%s)",
1706 nickname.ToUTF8().data(), libItemName.ToUTF8().data() );
1714 wxString finalLibItemName = libItemName;
1716 if( !existingChecksum.empty() && existingChecksum != newChecksum )
1722 finalLibItemName = AppendNumericSuffix( libItemName, suffix++ );
1723 std::string candidateChecksum;
1725 if( !ComputeSymbolChecksum(
nullptr, outFile, finalLibItemName, candidateChecksum ) )
1731 if( candidateChecksum == newChecksum )
1734 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
1735 "receiveSymbol: symbol %s exists with same content as %s, skipping",
1736 finalLibItemName.ToUTF8().data(), libItemName.ToUTF8().data() );
1738 if( placeAfterDownload )
1745 downloadedSymbol->SetName( finalLibItemName );
1747 downloadedSymbol->SetLibId( savedId );
1749 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
1750 "receiveSymbol: renamed symbol from %s to %s due to content difference",
1751 libItemName.ToUTF8().data(), finalLibItemName.ToUTF8().data() );
1754 if( adapter->
SaveSymbol( nickname, downloadedSymbol.get(),
true )
1757 aError =
_(
"Unable to save the downloaded symbol." );
1758 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
1759 "receiveSymbol: failed to save symbol %s to library %s",
1760 finalLibItemName.ToUTF8().data(), nickname.ToUTF8().data() );
1765 downloadedSymbol.release();
1766 libItemName = finalLibItemName;
1768 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
1769 "receiveSymbol: saved symbol %s into library %s", libItemName.ToUTF8().data(),
1770 nickname.ToUTF8().data() );
1778 if( placeAfterDownload )
1780 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receiveSymbol: placing symbol now (nickname=%s libItem=%s)",
1781 nickname.ToUTF8().data(), libItemName.ToUTF8().data() );
1792 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receiveSymbol: saved symbol (nickname=%s libItem=%s)" ,
1793 nickname.ToUTF8().data(), libItemName.ToUTF8().data() );
1800 const std::vector<uint8_t>& aPayload,
1803 const wxString mode =
jsonString( aParams,
"mode" );
1805 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receiveFootprint: mode=%s", mode.ToUTF8().data() );
1807 if( !mode.IsEmpty() && !mode.IsSameAs( wxS(
"SAVE" ),
false )
1808 && !mode.IsSameAs( wxS(
"PLACE" ),
false ) )
1810 aError = wxString::Format(
_(
"Unsupported transfer mode '%s'." ), mode );
1814 const wxString contentType =
jsonString( aParams,
"content_type" );
1816 if( !contentType.IsSameAs( wxS(
"KICAD_FOOTPRINT_V1" ),
false ) )
1818 aError =
_(
"Unsupported footprint payload type." );
1827 wxFileName fpRoot = baseDir;
1828 fpRoot.AppendDir( wxS(
"footprints" ) );
1830 if( !fpRoot.DirExists() && !fpRoot.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
1832 aError = wxString::Format(
_(
"Unable to create '%s'." ), fpRoot.GetFullPath() );
1836 wxString footprintName =
jsonString( aParams,
"name" );
1838 if( footprintName.IsEmpty() )
1846 wxFileName libDir = fpRoot;
1847 libDir.AppendDir( libNickname + wxS(
".pretty" ) );
1849 if( !libDir.DirExists() && !libDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
1851 aError = wxString::Format(
_(
"Unable to create '%s'." ), libDir.GetFullPath() );
1856 auto footprintPathFor = [&](
const wxString& aName ) -> wxFileName
1858 wxFileName fn( libDir );
1859 fn.SetFullName( aName + wxS(
".kicad_mod" ) );
1864 std::string newChecksum = HashBuffer( aPayload );
1867 wxFileName existingFile = footprintPathFor( footprintName );
1868 std::string existingChecksum;
1870 if( HashFile( existingFile, existingChecksum ) && existingChecksum == newChecksum )
1872 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
1873 "receiveFootprint: footprint %s already exists with same content, skipping",
1874 footprintName.ToUTF8().data() );
1879 wxString finalFootprintName = footprintName;
1881 if( !existingChecksum.empty() && existingChecksum != newChecksum )
1887 finalFootprintName = AppendNumericSuffix( footprintName, suffix++ );
1888 std::string candidateChecksum;
1889 wxFileName candidateFile = footprintPathFor( finalFootprintName );
1891 if( !HashFile( candidateFile, candidateChecksum ) )
1897 if( candidateChecksum == newChecksum )
1900 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
1901 "receiveFootprint: footprint %s exists with same content, skipping",
1902 finalFootprintName.ToUTF8().data() );
1907 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
1908 "receiveFootprint: renamed footprint from %s to %s due to content difference",
1909 footprintName.ToUTF8().data(), finalFootprintName.ToUTF8().data() );
1912 wxFileName outFile = footprintPathFor( finalFootprintName );
1917 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receiveFootprint: wrote footprint %s in lib %s",
1918 outFile.GetFullPath(), libNickname.ToUTF8().data() );
1924 aError =
_(
"Unable to load schematic settings." );
2066 const std::vector<uint8_t>& aPayload,
2067 wxString& aError, nlohmann::json* aResponseParams )
2069 nlohmann::json components;
2073 components = nlohmann::json::parse( aPayload.begin(), aPayload.end() );
2075 catch(
const std::exception& e )
2077 aError = wxString::Format(
_(
"Failed to parse component list: %s" ), e.what() );
2081 if( !components.is_array() )
2083 aError =
_(
"Component list must be an array." );
2087 if( components.empty() )
2089 aError =
_(
"Component list was empty." );
2096 if( libNickname.IsEmpty() )
2097 libNickname = wxS(
"Remote" );
2104 auto ensureDirectory = [&]( wxFileName& aDir ) ->
bool
2106 if( aDir.DirExists() )
2109 if( !aDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
2111 aError = wxString::Format(
_(
"Unable to create '%s'." ), aDir.GetFullPath() );
2118 wxFileName librariesDir( baseDir );
2119 librariesDir.AppendDir( wxS(
"symbols" ) );
2121 if( !ensureDirectory( librariesDir ) )
2124 wxFileName symbolLib( librariesDir );
2125 symbolLib.SetFullName( libNickname + wxS(
".kicad_sym" ) );
2127 wxFileName footprintsDir( baseDir );
2128 footprintsDir.AppendDir( wxS(
"footprints" ) );
2130 if( !ensureDirectory( footprintsDir ) )
2133 wxFileName footprintLibDir( footprintsDir );
2134 footprintLibDir.AppendDir( libNickname + wxS(
".pretty" ) );
2136 if( !ensureDirectory( footprintLibDir ) )
2139 wxFileName modelDir( baseDir );
2140 modelDir.AppendDir( wxS(
"3dmodels" ) );
2142 if( !ensureDirectory( modelDir ) )
2145 modelDir.AppendDir( libNickname );
2147 if( !ensureDirectory( modelDir ) )
2154 aError =
_(
"Unable to load schematic settings." );
2160 std::unique_ptr<SCH_IO> symbolPlugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
2164 aError =
_(
"Unable to access the KiCad symbol plugin." );
2168 struct COMPONENT_PAYLOAD
2171 wxString declaredName;
2172 wxString sanitizedName;
2174 wxString payloadSymbolName;
2175 std::string checksum;
2176 std::vector<uint8_t> content;
2180 std::vector<COMPONENT_PAYLOAD> prepared;
2181 prepared.reserve( components.size() );
2183 for(
const nlohmann::json& component : components )
2185 if( !component.is_object() )
2187 aError =
_(
"Component entries must be objects." );
2191 COMPONENT_PAYLOAD entry;
2192 entry.type = component.value(
"type",
"" );
2194 if( entry.type.empty() )
2196 aError =
_(
"Component entry was missing a type." );
2200 std::transform( entry.type.begin(), entry.type.end(), entry.type.begin(),
2201 [](
unsigned char c ) { return static_cast<char>( std::tolower( c ) ); } );
2203 entry.declaredName = wxString::FromUTF8( component.value(
"name", entry.type ).c_str() );
2204 entry.declaredName.Trim(
true ).Trim(
false );
2206 wxString fallback =
sanitizedPrefix() + wxS(
"_" ) + wxString::FromUTF8( entry.type );
2209 if( entry.sanitizedName.IsEmpty() )
2210 entry.sanitizedName = fallback;
2212 entry.finalName = entry.sanitizedName;
2213 entry.payloadSymbolName = entry.declaredName.IsEmpty() ? entry.sanitizedName : entry.declaredName;
2215 const std::string encoded = component.value(
"content", std::string() );
2220 const std::string compression = component.value(
"compression", std::string() );
2222 if( !compression.empty() && compression !=
"NONE" )
2224 std::vector<uint8_t> decompressed;
2229 entry.content = std::move( decompressed );
2232 entry.checksum = component.value(
"checksum", std::string() );
2234 if( entry.checksum.empty() )
2235 entry.checksum = HashBuffer( entry.content );
2237 prepared.emplace_back( std::move( entry ) );
2240 nlohmann::json skipped = nlohmann::json::array();
2241 nlohmann::json renamedReport = nlohmann::json::array();
2242 std::map<std::string, std::map<wxString, wxString>> renames;
2243 std::set<wxString> reservedSymbolNames;
2244 std::set<wxString> reservedFootprintNames;
2245 std::set<wxString> reservedModelNames;
2247 auto recordRename = [&](
const std::string& aType,
const wxString& aFrom,
const wxString& aTo )
2252 renames[aType][aFrom] = aTo;
2254 renamedReport.push_back( { {
"type", aType },
2255 {
"from", aFrom.ToStdString() },
2256 {
"to", aTo.ToStdString() } } );
2259 auto recordSkip = [&](
const std::string& aType,
const wxString& aName )
2261 skipped.push_back( { {
"type", aType }, {
"name", aName.ToStdString() } } );
2264 auto footprintPathFor = [&](
const wxString& aName )
2266 wxFileName fn( footprintLibDir );
2267 fn.SetFullName( aName + wxS(
".kicad_mod" ) );
2271 auto modelPathFor = [&](
const wxString& aName )
2273 wxFileName fn( modelDir );
2274 fn.SetFullName( aName );
2278 for( COMPONENT_PAYLOAD& entry : prepared )
2280 if( entry.type ==
"footprint" )
2282 wxFileName existing = footprintPathFor( entry.sanitizedName );
2283 std::string localChecksum;
2285 if( HashFile( existing, localChecksum ) && localChecksum == entry.checksum )
2288 recordSkip( entry.type, entry.sanitizedName );
2292 wxString candidate = entry.sanitizedName;
2295 auto collides = [&](
const wxString&
name )
2297 if( reservedFootprintNames.contains(
name ) )
2300 return footprintPathFor(
name ).FileExists();
2303 while( collides( candidate ) )
2304 candidate = AppendNumericSuffix( entry.sanitizedName, suffix++ );
2306 reservedFootprintNames.insert( candidate );
2307 recordRename( entry.type, entry.sanitizedName, candidate );
2308 entry.finalName = candidate;
2310 else if( entry.type ==
"3dmodel" )
2312 wxFileName existing = modelPathFor( entry.sanitizedName );
2313 std::string localChecksum;
2315 if( HashFile( existing, localChecksum ) && localChecksum == entry.checksum )
2318 recordSkip( entry.type, entry.sanitizedName );
2322 wxString candidate = entry.sanitizedName;
2325 auto collides = [&](
const wxString&
name )
2327 if( reservedModelNames.contains(
name ) )
2330 return modelPathFor(
name ).FileExists();
2333 while( collides( candidate ) )
2334 candidate = AppendNumericSuffixToFilename( entry.sanitizedName, suffix++ );
2336 reservedModelNames.insert( candidate );
2337 recordRename( entry.type, entry.sanitizedName, candidate );
2338 entry.finalName = candidate;
2340 else if( entry.type ==
"symbol" )
2342 std::string localChecksum;
2344 if( ComputeSymbolChecksum( symbolPlugin.get(), symbolLib, entry.sanitizedName, localChecksum )
2345 && localChecksum == entry.checksum )
2348 recordSkip( entry.type, entry.sanitizedName );
2352 wxString candidate = entry.sanitizedName;
2355 auto exists = [&](
const wxString&
name )
2357 if( reservedSymbolNames.contains(
name ) )
2360 std::unique_ptr<LIB_SYMBOL> symbol = TryCloneSymbol( symbolPlugin.get(), symbolLib,
name );
2361 return static_cast<bool>( symbol );
2364 while( exists( candidate ) )
2365 candidate = AppendNumericSuffix( entry.sanitizedName, suffix++ );
2367 reservedSymbolNames.insert( candidate );
2368 recordRename( entry.type, entry.sanitizedName, candidate );
2369 entry.finalName = candidate;
2373 aError = wxString::Format(
_(
"Unsupported component type '%s'." ),
2374 wxString::FromUTF8( entry.type ) );
2379 bool footprintLibraryReady =
false;
2380 bool symbolLibraryReady =
false;
2382 auto ensureFootprintLibrary = [&]() ->
bool
2384 if( footprintLibraryReady )
2390 footprintLibraryReady =
true;
2394 auto ensureSymbolLibrary = [&]() ->
bool
2396 if( symbolLibraryReady )
2402 symbolLibraryReady =
true;
2406 for( COMPONENT_PAYLOAD& entry : prepared )
2411 if( entry.type ==
"footprint" )
2413 if( !ensureFootprintLibrary() )
2416 if( !renames[
"3dmodel"].
empty() )
2418 std::string contentStr( entry.content.begin(), entry.content.end() );
2420 for(
const auto& [oldModel, newModel] : renames[
"3dmodel"] )
2422 const std::string from = oldModel.ToStdString();
2423 const std::string to = newModel.ToStdString();
2424 size_t pos = contentStr.find( from );
2426 while( pos != std::string::npos )
2428 contentStr.replace( pos, from.size(), to );
2429 pos = contentStr.find( from, pos + to.size() );
2433 entry.content.assign( contentStr.begin(), contentStr.end() );
2436 wxFileName target = footprintPathFor( entry.finalName );
2441 else if( entry.type ==
"3dmodel" )
2443 wxFileName target = modelPathFor( entry.finalName );
2445 if( !target.GetPath().IsEmpty() )
2447 wxFileName parent( target.GetPath(), wxEmptyString );
2449 if( !parent.DirExists() && !parent.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
2451 aError = wxString::Format(
_(
"Unable to create '%s'." ), parent.GetFullPath() );
2459 else if( entry.type ==
"symbol" )
2461 if( !ensureSymbolLibrary() )
2464 if( !renames[
"footprint"].
empty() )
2466 std::string contentStr( entry.content.begin(), entry.content.end() );
2468 for(
const auto& [oldFp, newFp] : renames[
"footprint"] )
2470 const std::string from = oldFp.ToStdString();
2471 const std::string to = newFp.ToStdString();
2472 size_t pos = contentStr.find( from );
2474 while( pos != std::string::npos )
2476 contentStr.replace( pos, from.size(), to );
2477 pos = contentStr.find( from, pos + to.size() );
2481 entry.content.assign( contentStr.begin(), contentStr.end() );
2485 entry.payloadSymbolName,
2491 if( entry.finalName != entry.sanitizedName )
2492 symbol->SetName( entry.finalName );
2496 if( !symbolLib.FileExists() )
2497 symbolPlugin->SaveLibrary( symbolLib.GetFullPath() );
2499 symbolPlugin->SaveSymbol( symbolLib.GetFullPath(), symbol.get() );
2504 aError = ioe.
What();
2510 if( aResponseParams )
2512 nlohmann::json response = nlohmann::json::object();
2514 if( !skipped.empty() )
2515 response[
"skipped"] = skipped;
2517 if( !renamedReport.empty() )
2518 response[
"renamed"] = renamedReport;
2520 *aResponseParams = std::move( response );