<?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: Himanshu Neema</title>
    <description>The latest articles on DEV Community by Himanshu Neema (@64bit).</description>
    <link>https://dev.to/64bit</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%2F480818%2F1c9e428d-edca-486f-a3cd-71920e5d9d31.png</url>
      <title>DEV Community: Himanshu Neema</title>
      <link>https://dev.to/64bit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/64bit"/>
    <language>en</language>
    <item>
      <title>What is a Serverless VPN?</title>
      <dc:creator>Himanshu Neema</dc:creator>
      <pubDate>Sun, 07 Jan 2024 08:00:00 +0000</pubDate>
      <link>https://dev.to/64bit/what-is-a-serverless-vpn-537h</link>
      <guid>https://dev.to/64bit/what-is-a-serverless-vpn-537h</guid>
      <description>&lt;h2&gt;
  
  
  Serverless VPN
&lt;/h2&gt;

&lt;p&gt;Serverless VPN is a fusion of two concepts: Serverless Computing and VPN.&lt;br&gt;
To understand, lets dive into the idea behind Serverless Computing and how it can be applied to &lt;a href="https://upvpn.app/blog/post/what-is-a-vpn"&gt;VPN&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Serverless Computing
&lt;/h3&gt;

&lt;p&gt;Serverless Computing is a method of using Servers efficiently. Yes, there are Servers still used.&lt;/p&gt;

&lt;p&gt;Modern cloud providers host powerful servers in data centers around the world. To efficiently utilize each physical server, its resources are sliced into smaller units called Virtual Machines, or VMs in short.&lt;/p&gt;

&lt;p&gt;Every physical resource is virtualized - storage, CPU, memory and even network. This way, many Virtual Machines with different Operating Systems can run simultaneously on same physical server with isolation from each other for security.&lt;/p&gt;

&lt;p&gt;Cloud providers offer these VMs to their customers via an API (An API, in this context, enables a computer to communicate with another computer via the Internet). The API facilitates the on-demand creation of a Virtual Machine when needed and the deletion of it when not in use – effectively releasing any physical server resources it was consuming. This way, you only pay for the computing resources that the VM used during the time it was running.&lt;/p&gt;

&lt;p&gt;This concept of creating a "slice" and customizing the configuration of the "slice" in the form of virtual server (or in the form of container - another lightweight virtualization technology) from a physical server on-demand and releasing its resources on-demand is foundational for Serverless Computing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Applying Serverless Computing to VPN
&lt;/h3&gt;

&lt;p&gt;The process of connecting to VPN and disconnecting from it indicates that a VPN server is only required for the duration of a VPN connection.&lt;/p&gt;

&lt;p&gt;UpVPN leverages this connection boundary to seamlessly provision a VPN server when a user requests to connect to VPN, and deprovision the server when the user request to disconnect from the VPN.&lt;/p&gt;

&lt;p&gt;This process of selecting a location and connecting to VPN, followed by disconnection after usage, constitutes a single VPN session.&lt;/p&gt;

&lt;p&gt;UpVPN is the first commercial product to leverage Serverless Computing for the duration of a VPN session, giving birth to the world's first Serverless VPN!&lt;/p&gt;

&lt;h3&gt;
  
  
  Serverless Computing in other Domains
&lt;/h3&gt;

&lt;p&gt;Serverless as a term became more popular when the cloud provider AWS launched the AWS Lambda product in 2014, allowing applications to run without managing servers. Many tools, called "Function as a Service", have emerged, applying the Serverless model of computing to run application code. The Serverless model has also been applied to databases, leading to serverless databases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Serverless VPN
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cost Effective
&lt;/h3&gt;

&lt;p&gt;By efficiently utilizing servers, the cost benefit is passed on to consumers through inexpensive pricing models such as pay as you go pricing model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Time Savings
&lt;/h3&gt;

&lt;p&gt;Serverless VPN, like UpVPN, is a fully managed VPN service. Consumers can get an on-demand VPN server with a single click, single tap, or a single command line on the terminal. This saves time for technical consumers who like to run their own VPN servers, eliminating the hassles of managing their own servers and VPN configurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scalability
&lt;/h3&gt;

&lt;p&gt;Serverless VPNs, like UpVPN, leverage multiple cloud providers to offer numerous locations and can expand them in a short time.&lt;br&gt;
And relies on cloud provider APIs to scale available VPN servers on-demand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cold or Warm Location
&lt;/h3&gt;

&lt;p&gt;In computing, a 'cold start' or 'cold boot' is a term used to refer to the booting of a computing resource, such as a VM or container.&lt;/p&gt;

&lt;p&gt;For a fully serverless VPN location (referred to as a &lt;a href="https://upvpn.app/blog/post/warm-and-cold-vpn-locations/"&gt;cold location&lt;/a&gt;), the "cold start" manifests as a longer wait time before a server is ready for the consumer to connect to the VPN. Even though it takes more time, the consumer benefits from getting a dedicated VPN server along with a dedicated IP.&lt;/p&gt;

&lt;p&gt;Alternatively, when server capacity at a location is immediately available (referred to as a &lt;a href="https://upvpn.app/blog/post/warm-and-cold-vpn-locations/"&gt;warm location&lt;/a&gt;), a consumer can connect to the VPN immediately!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By applying Serverless Computing model to VPN, UpVPN has created a new kind of VPN and passed down its cost savings and time-saving benefits to consumers!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Ideas for crafting CLI in Rust</title>
      <dc:creator>Himanshu Neema</dc:creator>
      <pubDate>Fri, 30 Jun 2023 00:21:31 +0000</pubDate>
      <link>https://dev.to/64bit/ideas-for-crafting-cli-in-rust-5b0d</link>
      <guid>https://dev.to/64bit/ideas-for-crafting-cli-in-rust-5b0d</guid>
      <description>&lt;p&gt;I'm a big fan of Rust. My first real world usage of Rust was a cli I created for a hobby project.&lt;/p&gt;

&lt;p&gt;In this article I want to share few ideas I used to create cli called &lt;code&gt;upvpn&lt;/code&gt; for &lt;a href="https://upvpn.app" rel="noopener noreferrer"&gt;UpVPN&lt;/a&gt; to mange Serverless VPN sessions. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frnjgu5feh0ttymi8fdp5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frnjgu5feh0ttymi8fdp5.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Idea: Subcommand Pattern
&lt;/h3&gt;

&lt;p&gt;A lot of simple clis have following pattern: A cli with many sub-commands and each of the sub-command having its own arguments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;cli-name&amp;gt; &amp;lt;sub-command&amp;gt; [&amp;lt;arg&amp;gt;] ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Overtime we want to be able to add sub-command easily. &lt;/p&gt;

&lt;p&gt;To do so, lets create a mental model of adding a new sub-command to our cli:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Each sub-command is represented as an &lt;code&gt;enum&lt;/code&gt; variant. &lt;/li&gt;
&lt;li&gt;Arguments or data to a particular sub-command is represented as &lt;code&gt;struct&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;To execute sub-command we call &lt;code&gt;run()&lt;/code&gt; method on &lt;code&gt;struct&lt;/code&gt; holding all the arguments.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lets expand each point in detail:&lt;/p&gt;

&lt;h4&gt;
  
  
  Point #1 and #2
&lt;/h4&gt;

&lt;p&gt;First two are simple use cases of &lt;a href="https://clap.rs" rel="noopener noreferrer"&gt;clap.rs&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;clap&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Subcommand&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Parser,&lt;/span&gt; &lt;span class="nd"&gt;Debug)]&lt;/span&gt;
&lt;span class="nd"&gt;#[command(author,&lt;/span&gt; &lt;span class="nd"&gt;version,&lt;/span&gt; &lt;span class="nd"&gt;about)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Cli&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[clap(subcommand)]&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Subcommand,&lt;/span&gt; &lt;span class="nd"&gt;Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Commands&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/// Sign in to your https://upvpn.app account&lt;/span&gt;
    &lt;span class="nf"&gt;SignIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SignIn&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="cd"&gt;/// Sign out current device&lt;/span&gt;
    &lt;span class="nf"&gt;SignOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SignOut&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="cd"&gt;/// Current VPN status&lt;/span&gt;
    &lt;span class="nf"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="cd"&gt;/// Available locations for VPN&lt;/span&gt;
    &lt;span class="nf"&gt;Locations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ListLocations&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="cd"&gt;/// Connect VPN&lt;/span&gt;
    &lt;span class="nf"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="cd"&gt;/// Disconnect VPN&lt;/span&gt;
    &lt;span class="nf"&gt;Disconnect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Disconnect&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here each of the variants &lt;code&gt;SignIn&lt;/code&gt;, &lt;code&gt;Locations&lt;/code&gt; etc. will be available as sub-command on cli with lower case. And their arguments are stored as data in the &lt;code&gt;struct&lt;/code&gt; fields.&lt;/p&gt;

&lt;p&gt;For instance, &lt;code&gt;SignIn&lt;/code&gt; has optional argument to hold email of the user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;derive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;SignIn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Point #3
&lt;/h3&gt;

&lt;p&gt;We define a trait &lt;code&gt;RunCommand&lt;/code&gt; for each of the data structs to derive, so that we can execute the command by simply calling &lt;code&gt;run()&lt;/code&gt; on it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;RunCommand&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;CliError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For instance, &lt;code&gt;SignIn&lt;/code&gt; will be implemented it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;derive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;SignIn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;RunCommand&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;SignIn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;CliError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;// todo: implement sign in logic.&lt;/span&gt;
       &lt;span class="c1"&gt;// arguments to cli available here as data fields on self&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having this trait makes main driver of whole cli very simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Cli&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ExitCode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.command&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;SignIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sign_in&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sign_in&lt;/span&gt;&lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;SignOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sign_out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sign_out&lt;/span&gt;&lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Locations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list_locations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list_locations&lt;/span&gt;&lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Disconnect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;disconnect&lt;/span&gt;&lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// process error in output to return exit code&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Idea: Error messages for user
&lt;/h3&gt;

&lt;p&gt;Having &lt;code&gt;RunCommand&lt;/code&gt; trait with single method with return type &lt;code&gt;Result&amp;lt;(), CliError&amp;gt;&lt;/code&gt; has another benefit, because now we only need to implement &lt;code&gt;Display&lt;/code&gt; for &lt;code&gt;CliError&lt;/code&gt; for displaying errors in a user friendly way. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dtolnay/thiserror" rel="noopener noreferrer"&gt;thiserror&lt;/a&gt; crate makes it easy to do so using macros on &lt;code&gt;CliError&lt;/code&gt; enum.&lt;/p&gt;

&lt;h3&gt;
  
  
  Idea: Graceful Termination
&lt;/h3&gt;

&lt;p&gt;Certain cli operations may be long running in nature, in this situation cli user should be able to exit it gracefully by &lt;code&gt;SIGTERM&lt;/code&gt; or &lt;code&gt;ctrl+c&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To handle it would require a dedicated async task (or dedicated thread) to listen to these system events and notify rest of the system through oneshot or broadcast &lt;code&gt;channels&lt;/code&gt;, or as simple as printing a message and exiting whole process by &lt;code&gt;std::process::exit(code)&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Tokio's doc on the &lt;a href="https://tokio.rs/tokio/topics/shutdown" rel="noopener noreferrer"&gt;Shutdown topic&lt;/a&gt; is very informative. &lt;/p&gt;

&lt;h3&gt;
  
  
  Idea: Configuration
&lt;/h3&gt;

&lt;p&gt;Cli can take its global configuration from files (json, toml), environment variables or even combination of them. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sergiobenitez/figment" rel="noopener noreferrer"&gt;Figment&lt;/a&gt; makes merging configuration from various sources very easy. &lt;/p&gt;

&lt;h3&gt;
  
  
  Idea: Progress, Colors and User Input
&lt;/h3&gt;

&lt;p&gt;Colors and Progress of a cli not only make cli pleasant to use, but can convey important information to user about errors, success, and progress of long running operations. &lt;/p&gt;

&lt;p&gt;When asking user input from list of many options, make it easy to filter by typing few characters through &lt;code&gt;dialoguer::FuzzySelect&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;When asking user for password, keep it stay hidden using &lt;code&gt;dialoguer::Password&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.rs/dialoguer/latest/dialoguer/" rel="noopener noreferrer"&gt;dialoguer&lt;/a&gt;, &lt;a href="https://docs.rs/indicatif/latest/indicatif/" rel="noopener noreferrer"&gt;indicatif&lt;/a&gt;, and &lt;a href="https://docs.rs/console/latest/console/" rel="noopener noreferrer"&gt;console&lt;/a&gt; are your best friends for a good cli UX.&lt;/p&gt;

&lt;p&gt;That's all! For more, checkout this &lt;a href="https://rust-cli.github.io/book/index.html" rel="noopener noreferrer"&gt;book&lt;/a&gt; on building cli in Rust.&lt;/p&gt;

&lt;p&gt;To see it all together, checkout cli code in &lt;a href="https://github.com/upvpn/upvpn-app/tree/main/upvpn-cli" rel="noopener noreferrer"&gt;upvpn-app&lt;/a&gt; Github repository.&lt;/p&gt;

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