KiCad PCB EDA Suite
Loading...
Searching...
No Matches
remote_symbol_import_job.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 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
21
23
24#include <eeschema_settings.h>
25#include <string_view>
27#include <pgm_base.h>
30
31#include <wx/intl.h>
32
33
34namespace
35{
36bool validateSymbolPayload( const std::vector<uint8_t>& aPayload, const wxString& aExpectedSymbolName,
37 wxString& aError )
38{
39 if( aExpectedSymbolName.IsEmpty() )
40 return true;
41
42 if( aPayload.empty() )
43 {
44 aError = _( "Downloaded symbol payload was empty." );
45 return false;
46 }
47
48 std::string_view payload( reinterpret_cast<const char*>( aPayload.data() ), aPayload.size() );
49
50 if( payload.find( "(kicad_symbol_lib" ) == std::string_view::npos )
51 {
52 aError = _( "Downloaded symbol payload was not a KiCad symbol library." );
53 return false;
54 }
55
56 const std::string symbolToken =
57 wxString::Format( wxS( "(symbol \"%s\"" ), aExpectedSymbolName ).ToStdString();
58
59 if( payload.find( symbolToken ) == std::string_view::npos )
60 {
61 aError = wxString::Format(
62 _( "Downloaded symbol payload did not include expected symbol '%s'." ),
63 aExpectedSymbolName );
64 return false;
65 }
66
67 return true;
68}
69} // namespace
70
71
73 REMOTE_SYMBOL_DOWNLOAD_MANAGER* aDownloader ) :
74 m_frame( aFrame ),
75 m_downloader( aDownloader )
76{
77 if( !m_downloader )
78 {
79 m_ownedDownloader = std::make_unique<REMOTE_SYMBOL_DOWNLOAD_MANAGER>();
81 }
82}
83
84
86 const REMOTE_SYMBOL_IMPORT_CONTEXT& aContext,
87 const REMOTE_PROVIDER_PART_MANIFEST& aManifest,
88 bool aPlaceSymbol, wxString& aError )
89{
90 aError.clear();
91
93
94 if( !settings )
95 {
96 aError = _( "Unable to load schematic settings." );
97 return false;
98 }
99
100 wxFileName baseDir;
101
102 if( !EnsureRemoteDestinationRoot( baseDir, aError ) )
103 return false;
104
105 long long remainingBudget = aProvider.max_download_bytes;
106 const bool addToGlobal = settings->m_RemoteSymbol.add_to_global_table;
107 const bool strictLibraryTables = m_frame != nullptr;
108 const wxString prefix = RemoteLibraryPrefix();
109 bool importedSymbol = false;
110 wxString placedNickname;
111 wxString placedSymbolName;
112
113 for( const REMOTE_PROVIDER_PART_ASSET& asset : aManifest.assets )
114 {
116
117 if( !downloader().DownloadAndVerify( aProvider, asset, remainingBudget, fetched, aError ) )
118 return false;
119
120 remainingBudget -= asset.size_bytes;
121
122 if( asset.asset_type == wxS( "symbol" ) )
123 {
124 wxFileName symbolDir = baseDir;
125 symbolDir.AppendDir( wxS( "symbols" ) );
126
127 const wxString libraryName = SanitizeRemoteFileComponent(
128 asset.target_library.IsEmpty() ? aContext.library_name : asset.target_library,
129 wxS( "symbols" ), true );
130 const wxString symbolName =
131 asset.target_name.IsEmpty() ? aContext.symbol_name : asset.target_name;
132 const wxString nickname = prefix + wxS( "_" ) + libraryName;
133
134 wxFileName outFile( symbolDir );
135 outFile.SetFullName( nickname + wxS( ".kicad_sym" ) );
136
137 if( !validateSymbolPayload( fetched.payload, symbolName, aError ) )
138 return false;
139
140 if( !WriteRemoteBinaryFile( outFile, fetched.payload, aError ) )
141 return false;
142
143 if( strictLibraryTables )
144 {
146 addToGlobal, true, aError ) )
147 return false;
148
150 const LIBRARY_TABLE_SCOPE scope =
152
153 libMgr.ReloadLibraryEntry( LIBRARY_TABLE_TYPE::SYMBOL, nickname, scope );
154
155 // Force the library to LOADED state so GetLibSymbol and the symbol chooser
156 // can see the new symbol immediately. ReloadLibraryEntry leaves it in LOADING.
158 }
159
160 importedSymbol = true;
161 placedNickname = nickname;
162 placedSymbolName = symbolName;
163 }
164 else if( asset.asset_type == wxS( "footprint" ) )
165 {
166 wxFileName fpRoot = baseDir;
167 fpRoot.AppendDir( wxS( "footprints" ) );
168
169 const wxString libraryName = SanitizeRemoteFileComponent(
170 asset.target_library.IsEmpty() ? asset.name : asset.target_library,
171 wxS( "footprints" ), true );
172 const wxString nickname = prefix + wxS( "_" ) + libraryName;
173
174 wxFileName libDir = fpRoot;
175 libDir.AppendDir( nickname + wxS( ".pretty" ) );
176
177 wxString fileName = SanitizeRemoteFileComponent(
178 asset.target_name.IsEmpty() ? asset.name : asset.target_name,
179 wxS( "footprint" ) );
180
181 if( !fileName.Lower().EndsWith( wxS( ".kicad_mod" ) ) )
182 fileName += wxS( ".kicad_mod" );
183
184 wxFileName outFile( libDir );
185 outFile.SetFullName( fileName );
186
187 if( !WriteRemoteBinaryFile( outFile, fetched.payload, aError ) )
188 return false;
189
190 if( strictLibraryTables )
191 {
193 addToGlobal, true, aError ) )
194 return false;
195
197 const LIBRARY_TABLE_SCOPE scope =
199
200 libMgr.ReloadLibraryEntry( LIBRARY_TABLE_TYPE::FOOTPRINT, nickname, scope );
202 }
203 }
204 else if( asset.asset_type == wxS( "3dmodel" ) )
205 {
206 wxFileName modelDir = baseDir;
207 modelDir.AppendDir( prefix + wxS( "_3d" ) );
208
209 wxString fileName = SanitizeRemoteFileComponent(
210 asset.target_name.IsEmpty() ? asset.name : asset.target_name,
211 prefix + wxS( "_model" ) );
212
213 wxFileName outFile( modelDir );
214 outFile.SetFullName( fileName );
215
216 if( !WriteRemoteBinaryFile( outFile, fetched.payload, aError ) )
217 return false;
218 }
219 else if( asset.asset_type == wxS( "spice" ) )
220 {
221 wxFileName spiceDir = baseDir;
222 spiceDir.AppendDir( prefix + wxS( "_spice" ) );
223
224 wxString fileName = SanitizeRemoteFileComponent(
225 asset.target_name.IsEmpty() ? asset.name : asset.target_name,
226 prefix + wxS( "_model.cir" ) );
227
228 if( !fileName.Lower().EndsWith( wxS( ".cir" ) ) )
229 fileName += wxS( ".cir" );
230
231 wxFileName outFile( spiceDir );
232 outFile.SetFullName( fileName );
233
234 if( !WriteRemoteBinaryFile( outFile, fetched.payload, aError ) )
235 return false;
236 }
237 }
238
239 if( aPlaceSymbol )
240 {
241 if( !importedSymbol )
242 {
243 aError = _( "No symbol asset was available to place." );
244 return false;
245 }
246
247 if( !PlaceRemoteDownloadedSymbol( m_frame, placedNickname, placedSymbolName, aError ) )
248 return false;
249 }
250
251 return true;
252}
253
254
REMOTE_PROVIDER_SETTINGS m_RemoteSymbol
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.
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:132
const REMOTE_SYMBOL_DOWNLOAD_MANAGER & downloader() const
std::unique_ptr< REMOTE_SYMBOL_DOWNLOAD_MANAGER > m_ownedDownloader
REMOTE_SYMBOL_DOWNLOAD_MANAGER * m_downloader
REMOTE_SYMBOL_IMPORT_JOB(SCH_EDIT_FRAME *aFrame, REMOTE_SYMBOL_DOWNLOAD_MANAGER *aDownloader=nullptr)
bool Import(const REMOTE_PROVIDER_METADATA &aProvider, const REMOTE_SYMBOL_IMPORT_CONTEXT &aContext, const REMOTE_PROVIDER_PART_MANIFEST &aManifest, bool aPlaceSymbol, wxString &aError)
Schematic editor (Eeschema) main window.
#define _(s)
LIBRARY_TABLE_SCOPE
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
bool PlaceRemoteDownloadedSymbol(SCH_EDIT_FRAME *aFrame, const wxString &aNickname, const wxString &aLibItemName, wxString &aError)
Place a symbol from a remote download into the schematic editor.
bool EnsureRemoteLibraryEntry(LIBRARY_TABLE_TYPE aTableType, const wxFileName &aLibraryPath, const wxString &aNickname, bool aGlobalTable, bool aStrict, wxString &aError)
Add or update a library table entry for a remote download library.
bool WriteRemoteBinaryFile(const wxFileName &aOutput, const std::vector< uint8_t > &aPayload, wxString &aError)
Write binary data to a file, creating parent directories as needed.
bool EnsureRemoteDestinationRoot(wxFileName &aOutDir, wxString &aError)
Resolve and create the configured destination root directory for remote symbol downloads.
wxString RemoteLibraryPrefix()
Return the configured (or default) library prefix for remote downloads, sanitized for use as a filena...
wxString SanitizeRemoteFileComponent(const wxString &aValue, const wxString &aDefault, bool aLower)
Replace non-alphanumeric characters (other than _ - .) with underscores.
T * GetAppSettings(const char *aFilename)
std::vector< REMOTE_PROVIDER_PART_ASSET > assets