77// the Business Source License, use of this software will be governed
88// by the Apache License, Version 2.0.
99
10- use std:: collections:: { HashSet , LinkedList } ;
1110use std:: sync:: Arc ;
1211
12+ use internal_error:: * ;
1313use kamu:: domain:: * ;
1414use kamu_datasets:: {
1515 CreateDatasetFromSnapshotError ,
1616 CreateDatasetFromSnapshotUseCase ,
17- CreateDatasetResult ,
1817 CreateDatasetUseCaseOptions ,
1918 DeleteDatasetUseCase ,
2019} ;
@@ -40,6 +39,9 @@ pub struct AddCommand {
4039 #[ dill:: component( explicit) ]
4140 name : Option < odf:: DatasetAlias > ,
4241
42+ #[ dill:: component( explicit) ]
43+ dry_run : bool ,
44+
4345 #[ dill:: component( explicit) ]
4446 recursive : bool ,
4547
@@ -129,70 +131,6 @@ impl AddCommand {
129131
130132 Ok ( false )
131133 }
132-
133- pub async fn create_datasets_from_snapshots (
134- & self ,
135- snapshots : Vec < odf:: DatasetSnapshot > ,
136- create_options : CreateDatasetUseCaseOptions ,
137- ) -> Vec < (
138- odf:: DatasetAlias ,
139- Result < CreateDatasetResult , CreateDatasetFromSnapshotError > ,
140- ) > {
141- let snapshots_ordered =
142- self . sort_snapshots_in_dependency_order ( snapshots. into_iter ( ) . collect ( ) ) ;
143-
144- let mut ret = Vec :: new ( ) ;
145- for snapshot in snapshots_ordered {
146- let alias = snapshot. name . clone ( ) ;
147- let res = self
148- . create_dataset_from_snapshot
149- . execute ( snapshot, create_options)
150- . await ;
151-
152- ret. push ( ( alias, res) ) ;
153- }
154- ret
155- }
156-
157- #[ allow( clippy:: linkedlist) ]
158- fn sort_snapshots_in_dependency_order (
159- & self ,
160- mut snapshots : LinkedList < odf:: DatasetSnapshot > ,
161- ) -> Vec < odf:: DatasetSnapshot > {
162- let mut ordered = Vec :: with_capacity ( snapshots. len ( ) ) ;
163- let mut pending: HashSet < odf:: DatasetRef > =
164- snapshots. iter ( ) . map ( |s| s. name . clone ( ) . into ( ) ) . collect ( ) ;
165- let mut added: HashSet < odf:: DatasetAlias > = HashSet :: new ( ) ;
166-
167- // TODO: cycle detection
168- while !snapshots. is_empty ( ) {
169- let snapshot = snapshots. pop_front ( ) . unwrap ( ) ;
170-
171- use odf:: metadata:: EnumWithVariants ;
172- let transform = snapshot
173- . metadata
174- . iter ( )
175- . find_map ( |e| e. as_variant :: < odf:: metadata:: SetTransform > ( ) ) ;
176-
177- let has_pending_deps = if let Some ( transform) = transform {
178- transform. inputs . iter ( ) . any ( |input| {
179- pending. contains ( & input. dataset_ref )
180- && snapshot. name . as_local_ref ( ) != input. dataset_ref // Check for circular dependency
181- } )
182- } else {
183- false
184- } ;
185-
186- if !has_pending_deps {
187- pending. remove ( & snapshot. name . clone ( ) . into ( ) ) ;
188- added. insert ( snapshot. name . clone ( ) ) ;
189- ordered. push ( snapshot) ;
190- } else {
191- snapshots. push_back ( snapshot) ;
192- }
193- }
194- ordered
195- }
196134}
197135
198136////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -252,6 +190,7 @@ impl Command for AddCommand {
252190 }
253191
254192 // Delete existing datasets if we are replacing
193+ // TODO: Move into use case?
255194 if self . replace {
256195 let mut already_exist = Vec :: new ( ) ;
257196 for s in & snapshots {
@@ -277,61 +216,87 @@ impl Command for AddCommand {
277216 }
278217 } ;
279218
280- let create_options = CreateDatasetUseCaseOptions {
281- dataset_visibility : self . dataset_visibility ,
282- } ;
283- let mut add_results = self
284- . create_datasets_from_snapshots ( snapshots, create_options)
285- . await ;
286-
287- add_results. sort_by ( |( id_a, _) , ( id_b, _) | id_a. cmp ( id_b) ) ;
288-
289- let mut num_added = 0 ;
219+ let mut plan = self
220+ . create_dataset_from_snapshot
221+ . prepare (
222+ snapshots,
223+ CreateDatasetUseCaseOptions {
224+ dataset_visibility : self . dataset_visibility ,
225+ } ,
226+ )
227+ . await ?;
290228
291229 let mut errors_with_contexts = Vec :: new ( ) ;
230+ let mut errors = Vec :: new ( ) ;
231+ std:: mem:: swap ( & mut plan. errors , & mut errors) ;
292232
293- for ( id, res) in add_results {
294- match res {
295- Ok ( _) => {
296- num_added += 1 ;
297-
298- if !self . output_config . quiet {
299- eprintln ! ( "{}: {}" , console:: style( "Added" ) . green( ) , id) ;
300- }
301- }
302- Err ( CreateDatasetFromSnapshotError :: NameCollision ( _) ) => {
233+ for ( snaphot, err) in errors {
234+ match err {
235+ CreateDatasetFromSnapshotError :: NameCollision ( _) => {
303236 if !self . output_config . quiet {
304237 eprintln ! (
305238 "{}: {}: Already exists" ,
306239 console:: style( "Skipped" ) . yellow( ) ,
307- id
240+ snaphot . name ,
308241 ) ;
309242 }
310243 }
311- Err ( err) => {
312- errors_with_contexts. push ( ( err, format ! ( "Failed to add dataset {id}" ) ) ) ;
244+ err => {
245+ errors_with_contexts
246+ . push ( ( err, format ! ( "Failed to add dataset {}" , snaphot. name) ) ) ;
313247 }
314248 }
315249 }
316250
317- if errors_with_contexts. is_empty ( ) {
318- if !self . output_config . quiet {
251+ if !errors_with_contexts. is_empty ( ) {
252+ return Err ( BatchError :: new (
253+ format ! ( "Failed to add {} manifest(s)" , errors_with_contexts. len( ) ) ,
254+ errors_with_contexts,
255+ )
256+ . into ( ) ) ;
257+ }
258+
259+ if self . dry_run {
260+ let plan = serde_yaml:: to_string ( & plan) . int_err ( ) ?;
261+ eprintln ! (
262+ "{}\n {}\n {}" ,
263+ console:: style( "Execution plan:" ) . yellow( ) . bold( ) ,
264+ console:: style( & plan) . dim( ) ,
265+ console:: style( "Exiting early due to --dry-run" )
266+ . yellow( )
267+ . bold( )
268+ ) ;
269+ return Ok ( ( ) ) ;
270+ }
271+
272+ let mut add_results = self
273+ . create_dataset_from_snapshot
274+ . apply ( plan)
275+ . await
276+ . int_err ( ) ?;
277+
278+ add_results. sort_by ( |a, b| a. dataset_handle . alias . cmp ( & b. dataset_handle . alias ) ) ;
279+
280+ if !self . output_config . quiet {
281+ for res in & add_results {
319282 eprintln ! (
320- "{}" ,
321- console:: style( format!( "Added {num_added} dataset(s)" ) )
322- . green( )
323- . bold( )
283+ "{}: {}" ,
284+ console:: style( "Added" ) . green( ) ,
285+ res. dataset_handle. alias
324286 ) ;
325287 }
288+ }
326289
327- Ok ( ( ) )
328- } else {
329- Err ( BatchError :: new (
330- format ! ( "Failed to load {} manifest (s)" , errors_with_contexts . len( ) ) ,
331- errors_with_contexts ,
332- )
333- . into ( ) )
290+ if ! self . output_config . quiet {
291+ eprintln ! (
292+ "{}" ,
293+ console :: style ( format!( "Added {} dataset (s)" , add_results . len( ) ) )
294+ . green ( )
295+ . bold ( )
296+ ) ;
334297 }
298+
299+ Ok ( ( ) )
335300 }
336301}
337302
0 commit comments