@@ -35,6 +35,7 @@ use tmc_langs_util::{
3535} ;
3636use toml:: { map:: Map as TomlMap , Value as TomlValue } ;
3737use url:: Url ;
38+ use walkdir:: WalkDir ;
3839
3940#[ quit:: main]
4041fn main ( ) {
@@ -1415,7 +1416,7 @@ fn run_settings(
14151416 warnings : & [ anyhow:: Error ] ,
14161417) -> Result < PrintToken > {
14171418 let client_name = matches. value_of ( "client-name" ) . unwrap ( ) ;
1418- let mut map = config:: load_config ( client_name) ?;
1419+ let mut config = config:: load_config ( client_name) ?;
14191420
14201421 match matches. subcommand ( ) {
14211422 ( "get" , Some ( matches) ) => {
@@ -1425,37 +1426,156 @@ fn run_settings(
14251426 result : OutputResult :: RetrievedData ,
14261427 message : Some ( "Retrieved value" . to_string ( ) ) ,
14271428 percent_done : 1.0 ,
1428- data : map . get ( key) ,
1429+ data : Some ( config . get ( key) ) ,
14291430 } ) ;
14301431 print_output ( & output, pretty, warnings)
14311432 }
1432- ( "set" , Some ( matches) ) => {
1433- let key = matches. value_of ( "key" ) . unwrap ( ) ;
1434- let value = matches. value_of ( "json" ) . unwrap ( ) ;
1433+ ( "list" , Some ( _) ) => {
1434+ let output = Output :: OutputData ( OutputData {
1435+ status : Status :: Finished ,
1436+ result : OutputResult :: RetrievedData ,
1437+ message : Some ( "Retrieved settings" . to_string ( ) ) ,
1438+ percent_done : 1.0 ,
1439+ data : Some ( config) ,
1440+ } ) ;
1441+ print_output ( & output, pretty, warnings)
1442+ }
1443+ ( "move-projects-dir" , Some ( matches) ) => {
1444+ let dir = matches. value_of ( "dir" ) . unwrap ( ) ;
1445+ let target = PathBuf :: from ( dir) ;
14351446
1436- let value = serde_json:: from_str ( value)
1437- . with_context ( || format ! ( "Failed to deserialize {} as JSON" , value) ) ?;
1438- let value = json_to_toml ( value) ?;
1447+ if target. is_file ( ) {
1448+ anyhow:: bail!( "The target path points to a file." )
1449+ }
1450+ if !target. exists ( ) {
1451+ fs:: create_dir_all ( & target) . with_context ( || {
1452+ format ! ( "Failed to create directory at {}" , target. display( ) )
1453+ } ) ?;
1454+ }
1455+
1456+ let target_canon = target
1457+ . canonicalize ( )
1458+ . with_context ( || format ! ( "Failed to canonicalize {}" , target. display( ) ) ) ?;
1459+ let prev_dir_canon = config. projects_dir . canonicalize ( ) . with_context ( || {
1460+ format ! ( "Failed to canonicalize {}" , config. projects_dir. display( ) )
1461+ } ) ?;
1462+ if target_canon == prev_dir_canon {
1463+ anyhow:: bail!(
1464+ "Attempted to move the projects-dir to the directory it's already in."
1465+ )
1466+ }
1467+
1468+ let reporter = ProgressReporter :: new ( move |update| {
1469+ let output = Output :: StatusUpdate :: < ( ) > ( update) ;
1470+ print_output ( & output, pretty, & [ ] ) ?;
1471+ Ok ( ( ) )
1472+ } ) ;
1473+
1474+ reporter
1475+ . progress ( "Moving projects-dir" , 0.0 , None )
1476+ . map_err ( |e| anyhow:: anyhow!( e) ) ?;
1477+
1478+ let old_projects_dir = config. set_projects_dir ( target. clone ( ) ) ?;
1479+ let mut file_count_copied = 0 ;
1480+ let mut file_count_total = 0 ;
1481+ for entry in WalkDir :: new ( & old_projects_dir) {
1482+ let entry = entry. with_context ( || {
1483+ format ! ( "Failed to read file inside {}" , old_projects_dir. display( ) )
1484+ } ) ?;
1485+ if entry. path ( ) . is_file ( ) {
1486+ file_count_total += 1 ;
1487+ }
1488+ }
1489+ for entry in WalkDir :: new ( & old_projects_dir) . contents_first ( true ) {
1490+ let entry = entry. with_context ( || {
1491+ format ! ( "Failed to read file inside {}" , old_projects_dir. display( ) )
1492+ } ) ?;
1493+ let entry_path = entry. path ( ) ;
1494+
1495+ if entry_path. is_file ( ) {
1496+ let relative = entry_path. strip_prefix ( & old_projects_dir) . unwrap ( ) ;
1497+ let target_path = target. join ( relative) ;
1498+ log:: debug!(
1499+ "Moving {} -> {}" ,
1500+ entry_path. display( ) ,
1501+ target_path. display( )
1502+ ) ;
1503+
1504+ // create parent dir for target and copy it, remove source file after
1505+ if let Some ( parent) = target_path. parent ( ) {
1506+ fs:: create_dir_all ( parent) . with_context ( || {
1507+ format ! ( "Failed to create directory at {}" , parent. display( ) )
1508+ } ) ?;
1509+ }
1510+ fs:: copy ( entry_path, & target_path) . with_context ( || {
1511+ format ! (
1512+ "Failed to copy file from {} to {}" ,
1513+ entry_path. display( ) ,
1514+ target_path. display( )
1515+ )
1516+ } ) ?;
1517+ fs:: remove_file ( entry_path) . with_context ( || {
1518+ format ! (
1519+ "Failed to remove file at {} after copying it" ,
1520+ entry_path. display( )
1521+ )
1522+ } ) ?;
14391523
1440- map. insert ( key. to_string ( ) , value) ;
1441- config:: save_config ( client_name, map) ?;
1524+ file_count_copied += 1 ;
1525+ reporter
1526+ . progress (
1527+ format ! ( "Moved file {} / {}" , file_count_copied, file_count_total) ,
1528+ file_count_copied as f64 / file_count_total as f64 ,
1529+ None ,
1530+ )
1531+ . map_err ( |e| anyhow:: anyhow!( e) ) ?;
1532+ } else if entry_path. is_dir ( ) {
1533+ log:: debug!( "Deleting {}" , entry_path. display( ) ) ;
1534+ fs:: remove_dir ( entry_path) . with_context ( || {
1535+ format ! ( "Failed to remove directory at {}" , entry_path. display( ) )
1536+ } ) ?;
1537+ }
1538+ }
1539+
1540+ config:: save_config ( client_name, config) ?;
1541+
1542+ reporter
1543+ . finish_step ( "Finished moving project directory" , None )
1544+ . map_err ( |e| anyhow:: anyhow!( e) ) ?;
14421545
14431546 let output = Output :: < ( ) > :: OutputData ( OutputData {
14441547 status : Status :: Finished ,
14451548 result : OutputResult :: ExecutedCommand ,
1446- message : Some ( "Set setting " . to_string ( ) ) ,
1549+ message : Some ( "Moved project directory " . to_string ( ) ) ,
14471550 percent_done : 1.0 ,
14481551 data : None ,
14491552 } ) ;
14501553 print_output ( & output, pretty, warnings)
14511554 }
1452- ( "list" , Some ( _) ) => {
1453- let output = Output :: OutputData ( OutputData {
1555+ ( "set" , Some ( matches) ) => {
1556+ let key = matches. value_of ( "key" ) . unwrap ( ) ;
1557+ let value = matches. value_of ( "json" ) . unwrap ( ) ;
1558+
1559+ let value = match serde_json:: from_str ( value) {
1560+ Ok ( json) => json,
1561+ Err ( _) => {
1562+ // interpret as string
1563+ JsonValue :: String ( value. to_string ( ) )
1564+ }
1565+ } ;
1566+ let value = json_to_toml ( value) ?;
1567+
1568+ config
1569+ . insert ( key. to_string ( ) , value. clone ( ) )
1570+ . with_context ( || format ! ( "Failed to set {} to {}" , key, value) ) ?;
1571+ config:: save_config ( client_name, config) ?;
1572+
1573+ let output = Output :: < ( ) > :: OutputData ( OutputData {
14541574 status : Status :: Finished ,
1455- result : OutputResult :: RetrievedData ,
1456- message : Some ( "Retrieved settings " . to_string ( ) ) ,
1575+ result : OutputResult :: ExecutedCommand ,
1576+ message : Some ( "Set setting " . to_string ( ) ) ,
14571577 percent_done : 1.0 ,
1458- data : Some ( map ) ,
1578+ data : None ,
14591579 } ) ;
14601580 print_output ( & output, pretty, warnings)
14611581 }
@@ -1473,8 +1593,10 @@ fn run_settings(
14731593 }
14741594 ( "unset" , Some ( matches) ) => {
14751595 let key = matches. value_of ( "setting" ) . unwrap ( ) ;
1476- map. remove ( key) ;
1477- config:: save_config ( client_name, map) ?;
1596+ config
1597+ . remove ( key)
1598+ . with_context ( || format ! ( "Failed to unset {}" , key) ) ?;
1599+ config:: save_config ( client_name, config) ?;
14781600
14791601 let output = Output :: < ( ) > :: OutputData ( OutputData {
14801602 status : Status :: Finished ,
0 commit comments