KiCad PCB EDA Suite
Loading...
Searching...
No Matches
lib_merge_applier.h
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
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#ifndef LIB_MERGE_APPLIER_H
25#define LIB_MERGE_APPLIER_H
26
29
30#include <trace_helpers.h>
31
32#include <map>
33#include <memory>
34#include <set>
35#include <vector>
36
37#include <wx/log.h>
38#include <wx/string.h>
39
40
41namespace KICAD_DIFF
42{
43
57template <typename ITEM>
59{
60public:
61 using ITEM_MAP = std::map<wxString, const ITEM*>;
62
63 LIB_MERGE_APPLIER( const ITEM_MAP& aAncestor, const ITEM_MAP& aOurs, const ITEM_MAP& aTheirs, MERGE_PLAN aPlan ) :
64 m_ancestor( aAncestor ),
65 m_ours( aOurs ),
66 m_theirs( aTheirs ),
67 m_plan( std::move( aPlan ) )
68 {
69 }
70
71 struct REPORT
72 {
73 std::size_t itemsTakenOurs = 0;
74 std::size_t itemsTakenTheirs = 0;
75 std::size_t itemsTakenAncestor = 0;
76 std::size_t itemsDeleted = 0;
77 std::size_t itemsKept = 0;
78 std::size_t mergePropsFallback = 0;
79
84 std::vector<KIID_PATH> mergePropsFallbackIds;
85 };
86
87 std::vector<std::unique_ptr<ITEM>> Apply()
88 {
89 m_report = {};
90
91 std::map<wxString, const ITEM*> live = m_ancestor;
92
93 std::map<KIID_PATH, const ITEM_RESOLUTION*> actionsById;
94
95 for( const ITEM_RESOLUTION& res : m_plan.actions )
96 actionsById[res.id] = &res;
97
98 const std::set<wxString> allNames = collectNames( m_ancestor, m_ours, m_theirs );
99
100 for( const wxString& name : allNames )
101 {
102 auto resIt = actionsById.find( LibraryItemKiidPath( name ) );
103
104 if( resIt == actionsById.end() )
105 continue;
106
107 const ITEM_RESOLUTION& res = *resIt->second;
108
109 switch( res.kind )
110 {
111 case ITEM_RES::TAKE_OURS: takeFrom( m_ours, name, live, m_report.itemsTakenOurs ); break;
112 case ITEM_RES::TAKE_THEIRS: takeFrom( m_theirs, name, live, m_report.itemsTakenTheirs ); break;
113 case ITEM_RES::TAKE_ANCESTOR: takeFrom( m_ancestor, name, live, m_report.itemsTakenAncestor ); break;
114
116 live.erase( name );
117 ++m_report.itemsDeleted;
118 break;
119
120 case ITEM_RES::KEEP:
121 {
122 // KEEP is emitted for unresolved both-added collisions (and
123 // similar) where the engine wants the applier to do something
124 // safe. Walk ancestor -> ours -> theirs so an independent
125 // same-name add on both sides doesn't silently vanish.
126 const ITEM* src = findItem( m_ancestor, name );
127
128 if( !src )
129 src = findItem( m_ours, name );
130
131 if( !src )
132 src = findItem( m_theirs, name );
133
134 if( src )
135 live[name] = src;
136 else
137 live.erase( name );
138
139 ++m_report.itemsKept;
140 break;
141 }
142
144 // Per-property library-item merge would have to splice pins,
145 // fields, primitives, parent weak_ptr graphs — out of scope.
146 // Take ours so the chosen side at least lands intact, and
147 // surface the count + ID so the handler can rebuild the
148 // marker plan with this item flagged.
149 takeFrom( m_ours, name, live, m_report.mergePropsFallback );
150 m_report.mergePropsFallbackIds.push_back( res.id );
151 wxLogTrace( traceDiffMerge,
152 wxT( "lib_merge_applier: MERGE_PROPS for '%s' fell back to "
153 "TAKE_OURS" ),
154 name );
155 break;
156 }
157 }
158
159 std::vector<std::unique_ptr<ITEM>> out;
160 out.reserve( live.size() );
161
162 for( const auto& [name, item] : live )
163 {
164 if( item )
165 out.emplace_back( std::make_unique<ITEM>( *item ) );
166 }
167
168 return out;
169 }
170
171 const REPORT& GetReport() const { return m_report; }
172
173private:
174 static const ITEM* findItem( const ITEM_MAP& aMap, const wxString& aName )
175 {
176 auto it = aMap.find( aName );
177 return it == aMap.end() ? nullptr : it->second;
178 }
179
180 static std::set<wxString> collectNames( const ITEM_MAP& aAncestor, const ITEM_MAP& aOurs, const ITEM_MAP& aTheirs )
181 {
182 std::set<wxString> out;
183
184 for( const auto& [name, _] : aAncestor )
185 out.insert( name );
186 for( const auto& [name, _] : aOurs )
187 out.insert( name );
188 for( const auto& [name, _] : aTheirs )
189 out.insert( name );
190
191 return out;
192 }
193
194 static void takeFrom( const ITEM_MAP& aSource, const wxString& aName, std::map<wxString, const ITEM*>& aLive,
195 std::size_t& aCounter )
196 {
197 if( const ITEM* src = findItem( aSource, aName ) )
198 aLive[aName] = src;
199 else
200 aLive.erase( aName );
201
202 ++aCounter;
203 }
204
209 REPORT m_report;
210};
211
212} // namespace KICAD_DIFF
213
214#endif // LIB_MERGE_APPLIER_H
const char * name
static const ITEM * findItem(const ITEM_MAP &aMap, const wxString &aName)
static void takeFrom(const ITEM_MAP &aSource, const wxString &aName, std::map< wxString, const ITEM * > &aLive, std::size_t &aCounter)
const REPORT & GetReport() const
LIB_MERGE_APPLIER(const ITEM_MAP &aAncestor, const ITEM_MAP &aOurs, const ITEM_MAP &aTheirs, MERGE_PLAN aPlan)
std::map< wxString, const LIB_SYMBOL * > ITEM_MAP
std::vector< std::unique_ptr< ITEM > > Apply()
static std::set< wxString > collectNames(const ITEM_MAP &aAncestor, const ITEM_MAP &aOurs, const ITEM_MAP &aTheirs)
#define _(s)
const wxChar *const traceDiffMerge
Flag to enable diff/merge engine and renderer debugging output.
KIID_PATH LibraryItemKiidPath(const wxString &aName)
Build a deterministic synthetic KIID_PATH from a library item name (symbol name or footprint name).
STL namespace.
std::vector< KIID_PATH > mergePropsFallbackIds
Item IDs (KIID_PATH from LibraryItemKiidPath) for resolutions that the applier silently downgraded (c...
Result of planning a 3-way merge.
VECTOR3I res
wxLogTrace helper definitions.