<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ken Salter</title>
    <description>The latest articles on DEV Community by Ken Salter (@plecos).</description>
    <link>https://dev.to/plecos</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2778388%2Fddee7d6e-7e11-42b5-b573-4af0e8682de1.jpeg</url>
      <title>DEV Community: Ken Salter</title>
      <link>https://dev.to/plecos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/plecos"/>
    <language>en</language>
    <item>
      <title>A Duplicate File Finder in #Rust</title>
      <dc:creator>Ken Salter</dc:creator>
      <pubDate>Mon, 24 Feb 2025 12:59:19 +0000</pubDate>
      <link>https://dev.to/plecos/a-duplicate-file-finder-in-rust-17cj</link>
      <guid>https://dev.to/plecos/a-duplicate-file-finder-in-rust-17cj</guid>
      <description>&lt;p&gt;I set up a new storage system for my home network. I attached a 10TB drive, and proceeded to copy all my photos from every source I had. Also grabbed photos from my spouse, my parents...any source I could find.&lt;/p&gt;

&lt;p&gt;The end result was that I have quite a bit of duplicate photos resulting in a lot of wasted space.&lt;/p&gt;

&lt;p&gt;There are a lot of duplicate file finding software free for the download.  Since I'm trying to learn #rust, I decided to build one for myself.&lt;/p&gt;

&lt;p&gt;Thus was born &lt;strong&gt;dupefindr&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I included quite a few options, including the ability to move, copy, or delete duplicate files, wildcard file matching, multi-threaded for performance, interactivity to choose files to keep (as well as keep newest or oldest automatically), and more!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Usage: dupefindr [OPTIONS] &amp;lt;COMMAND&amp;gt;

Commands:
  find    Find duplicate files
  move    Move duplicate files to a new location
  copy    Copy duplicate files to a new location
  delete  Delete duplicate files
  help    Print this message or the help of the given subcommand(s)

Options:
  -p, --path &amp;lt;PATH&amp;gt;
          The directory to search for duplicates in [default: .]
  -w, --wildcard &amp;lt;WILDCARD&amp;gt;
          wildcard pattern to search for Example: *.txt [default: *]
      --exclusion-wildcard &amp;lt;EXCLUSION_WILDCARD&amp;gt;
          wildcard pattern to exclude fo Example: *.txt [default: ]
  -r, --recursive
          Recursively search for duplicates
      --debug
          Display debug information
  -0, --include-empty-files
          Include empty files
      --dry-run
          Dry run the program This will not delete or modify any files
  -H, --include-hidden-files
          Include hidden files
  -q, --quiet
          Hide progress indicators
  -v, --verbose
          Display verbose output
  -m, --max-threads &amp;lt;MAX_THREADS&amp;gt;
          Max threads to use Example: 4 Default: Number of CPUs If set to 0, then it will use the number of CPUs [default: 0]
  --create-report
          Create a report
  --report-path &amp;lt;REPORT_PATH&amp;gt;
          Path of the report Defaults to the folder where dupefindr was run [default: ./dupefindr-report.csv]
  -h, --help
          Print help
  -V, --version
          Print version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How does it work?  First, it collects all the files in the path you specify, recursively traversing sub folders if configured.  After all files have been collected, it generates a hash code for each file using MD5.  This hash code is a unique code based on the contents of the file.  Each hash code is compared to all the hash codes it has collected, and if it sees it in the collection, it knows it has found a duplicate!  Once this scan is complete, the program now knows all the files that are duplicates, and proceeds to process them by either copying, moving, or deleting the duplicates.  What it chooses to keep is based on either newest, oldest or interactive (e.g. the user chooses from a list).&lt;/p&gt;

&lt;p&gt;I suggest running it first with the --dry-run command line argument.  This will run the program but not make any changes to your file system.  You will be able to see what it would like do to your files.  And, as always, make sure you backup your data!&lt;/p&gt;

&lt;p&gt;You can find the project &lt;a href="https://github.com/plecos/dupefindr" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I'm quite new to Rust, I expect there are many improvements that can be made to the code.  Please feel free to create Pull Requests with your changes/additions!  And be kind, I'm still learning :-)&lt;/p&gt;

</description>
      <category>rust</category>
    </item>
    <item>
      <title>Handling CTRL-C while using crossterm</title>
      <dc:creator>Ken Salter</dc:creator>
      <pubDate>Tue, 28 Jan 2025 17:50:53 +0000</pubDate>
      <link>https://dev.to/plecos/handling-ctrl-c-while-using-crossterm-1kil</link>
      <guid>https://dev.to/plecos/handling-ctrl-c-while-using-crossterm-1kil</guid>
      <description>&lt;p&gt;Normally, I would use a crate called &lt;a href="https://docs.rs/ctrlc/latest/ctrlc/" rel="noopener noreferrer"&gt;ctrlc&lt;/a&gt; to gracefully handle when a user wants to stop my app with CTRL-C.&lt;/p&gt;

&lt;p&gt;In a recent app I wanted to implement a nice text UI for selecting items from a list.  I turned to the crate &lt;a href="https://docs.rs/crossterm/latest/crossterm/" rel="noopener noreferrer"&gt;crossterm&lt;/a&gt; to help with cursor positioning and keyboard events.&lt;/p&gt;

&lt;p&gt;In order to accomplish these goals, you must &lt;a href="https://docs.rs/crossterm/latest/crossterm/terminal/index.html#raw-mode" rel="noopener noreferrer"&gt;enable raw mode&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terminal::enable_raw_mode()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, this intercepts CTRL-C, and your ctrlc handler will never be called.&lt;/p&gt;

&lt;p&gt;My solution?&lt;/p&gt;

&lt;p&gt;The only workaround I could come up with is to implement a background thread to monitor keystrokes, and if we see a CTRL-C, then signal the main thread.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;std::thread::spawn(move || -&amp;gt; Result&amp;lt;()&amp;gt; {
        loop {
            if event::poll(std::time::Duration::from_millis(100))? {
                if let Event::Key(key_event) = event::read()? {
                    if key_event.code == KeyCode::Char('c') &amp;amp;&amp;amp; key_event.modifiers.contains(crossterm::event::KeyModifiers::CONTROL) {
                        // signal!
                        r.store(false, Ordering::SeqCst);
                    }
                }
            }
        }
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we use an Atomic boolean, but we can easily switch to messaging or some other means of communication.&lt;/p&gt;

&lt;p&gt;You can find the complete example project here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/plecos/ctrlc-crossterm" rel="noopener noreferrer"&gt;ctrlc-crossterm&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rust</category>
    </item>
  </channel>
</rss>
