Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Developer manual

Welcome to the developer manual for termscp. This chapter does NOT contain the documentation for termscp modules, which can instead be found on Rust Docs at https://docs.rs/termscp. This chapter describes how termscp works and the guidelines to implement features such as file transfers and additions to the user interface.

termscp is written in Rust (edition 2024, MSRV 1.89.0). The user interface is built with tuirealm v3, which runs on top of crossterm.

How termscp works

termscp is basically made up of 3 core modules:

  • The host: the host module provides functions to interact with file systems. It exposes the HostBridge trait, which abstracts file operations over both the local host (Localhost) and the remote host (RemoteBridged).
  • The ui: this module contains the implementation of the user interface. As shown in the next chapter, this is achieved through activities.
  • The activity_manager: the activity manager takes care of managing activities. It runs the activities of the user interface and chooses, based on their state, when to terminate the current activity and which activity to run next.

In addition to the 3 core modules, others have been added over time:

  • config: provides the configuration schema and its serialization methods.
  • explorer: exposes the explorer structures, which are used to handle the file explorer in the ui. They store the current directory model and the view states (e.g. sorting, whether to display hidden files, the transfer queue).
  • filetransfer: defines the FileTransferProtocol enum and the RemoteFsBuilder, which constructs the appropriate RemoteFs client from the connection parameters.
  • system: provides a way to interact with the configuration, the ssh key storage and the bookmarks.
  • utils: contains the utilities used by pretty much all of the project.

termscp supports the following protocols: SFTP, SCP, FTP/FTPS, Kube, S3, SMB and WebDAV.

Activities

This paragraph gives a short overview of activities. Read the code and the documentation for a clear idea of how the ui works.

There are many ways to implement a user interface. This project borrows what works best from different frameworks:

  • Activities on top: each “view” is an Activity, and an Activity Manager handles them. This approach is inspired by Android. It fits a ui that has different views, each one with its own components and logic. Activities work with the Context, which is a data holder used to share data between activities.
  • Activities display Applications: each activity can show different Applications. An application contains a View, which is basically a list of components, each one with its properties. The view is a facade to the components and also handles the focus, which is the current active component. You cannot have more than one active component, so this must be handled; at the same time, focus must be given back to the previously active component if the current one is destroyed. The Application takes care of all this. To learn more, read https://github.com/veeso/tui-realm.
  • Components: components are built around tui in order to reuse widgets. This is achieved through the Component trait, inspired by React. Each component has its Properties and can have its States. Each component must handle input events, accept new properties, and provide a method to render itself. This logic now lives in tui-realm.
  • Messages: an Elm-based approach: input events are handled with an approach inspired by Elm. In Elm you implement your ui using three basic functions: update, view and init. termscp implements the equivalent of the Elm update function as a large match case inside a recursive function, which you can find in the update.rs file inside each activity. This match case handles the messages produced by the components in response to incoming input events and causes the activity to change its state.

termscp implements a trait called Activity, a much reduced version of the Android activity. This trait provides these methods:

  • on_create: initializes the activity. The context is passed to the activity, which becomes the only owner of the Context until the activity terminates.
  • on_draw: called each time the user interface should be updated. This is basically the run method of the activity, and it also handles input events. The interface should not be drawn on every call (this method may be called hundreds of times per second), but only when something has actually changed (for example after an input event).
  • will_umount: returns whether the activity should be destroyed. If so, it returns an ExitReason, which indicates why the activity should terminate. Based on the reason, the activity manager chooses whether to stop the execution of termscp or to start a new activity, and which one.
  • on_destroy: finalizes the activity and drops it. This method returns the Context to the caller (the activity manager).

The Context

The context is a structure that holds data shared between activities. Every time an Activity starts, the Context is taken by the activity, until it is destroyed, where the context is finally returned to the activity manager. The context holds the following data:

  • The Localhost: the local host structure.
  • The File Transfer Params: the current parameters used to connect to the remote.
  • The Config Client: a structure that provides functions to access the user configuration.
  • The Store: a key-value storage that can hold any kind of data. It can be used to share state between activities or to keep persistence for heavy or slow tasks (such as checking for updates).
  • The Terminal: used to render the tui on the terminal.

Achieving an abstract file transfer client

When the implementation of termscp started, in December 2020, file transfer was at the core of the design, since it is at the heart of termscp. The first implementation consisted of a filetransfer module that exposed a trait called FileTransfer, which provided methods to generically interact with the remote file system.

This changed over time, as different users asked for a dedicated library. In the last quarter of 2021, remotefs was born: an abstract library to work with remote device file systems. remotefs provides a RemoteFs trait that exposes all of the core file-system functionalities, and since version 0.8.0 it has replaced the FileTransfer trait.

The file transfer module still exists, but its only task is to build a RemoteFs client implementation from the file transfer parameters through the RemoteFsBuilder.