diff --git a/src/cli.rs b/src/cli.rs index 88c597f..1698e9e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -12,6 +12,23 @@ use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; use reqwest::{Client, Error}; use tracing::{error, info}; +/// Creates a new `reqwest::Client` with default headers. +/// +/// This function fetches the `GITHUB_TOKEN` environment variable and uses it to set the `Authorization` header for the client. +/// +/// # Returns +/// +/// This function returns a `Result` that contains a `reqwest::Client` if the client was successfully created, or an `Error` if the client could not be created. +/// +/// # Example +/// +/// ```rust +/// let client = create_reqwest_client(); +/// ``` +/// +/// # Errors +/// +/// This function will return an error if the `reqwest::Client` could not be built. fn create_reqwest_client() -> Result { // fetch env variable let github_token = std::env::var("GITHUB_TOKEN"); @@ -32,6 +49,8 @@ fn create_reqwest_client() -> Result { Ok(client) } + +/// The `Cli` enum represents the different commands that can be used in the command-line interface. #[derive(Debug, Parser)] #[command(version)] enum Cli { @@ -86,6 +105,23 @@ enum Cli { Update(Update), } +/// Represents an update command in the CLI. +/// +/// This struct contains options for the update command, such as the version to update and whether to update all versions. +/// +/// # Fields +/// +/// * `version: Option` - The version to update. This can be either "nightly" or "stable". This field conflicts with the `all` field, meaning you can't specify a version and use `all` at the same time. +/// * `all: bool` - Whether to apply the update to all versions. If this is `true`, the `version` field must be `None`. +/// +/// # Example +/// +/// ```rust +/// let update = Update { +/// version: Some("nightly".to_string()), +/// all: false, +/// }; +/// ``` #[derive(Args, Debug)] pub struct Update { /// Update specified version |nightly|stable| @@ -97,6 +133,24 @@ pub struct Update { pub all: bool, } +/// Starts the CLI application. +/// +/// This function takes a `Config` object as input and returns a `Result`. It creates a Reqwest client, parses the CLI arguments, and then handles the arguments based on their type. +/// +/// # Arguments +/// +/// * `config: Config` - The configuration for the application. +/// +/// # Returns +/// +/// * `Result<()>` - Returns a `Result`. If the function completes successfully, it returns `Ok(())`. If an error occurs, it returns `Err`. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// start(config).await.unwrap(); +/// ``` pub async fn start(config: Config) -> Result<()> { let client = create_reqwest_client()?; let cli = Cli::parse(); diff --git a/src/config.rs b/src/config.rs index 3a270f6..35c3e5c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,6 +4,34 @@ use serde::{Deserialize, Serialize}; use std::env; use tokio::fs::{self}; +/// Represents the application configuration. +/// +/// This struct contains various configuration options for the application, such as whether to enable nightly info, whether to enable release build, the location for downloads, the location for installation, the location for the version sync file, the GitHub mirror to use, and the rollback limit. +/// +/// # Fields +/// +/// * `enable_nightly_info: Option` - Whether to enable nightly info. This is optional and may be `None`. +/// * `enable_release_build: Option` - Whether to enable release build. This is optional and may be `None`. +/// * `downloads_location: Option` - The location for downloads. This is optional and may be `None`. +/// * `installation_location: Option` - The location for installation. This is optional and may be `None`. +/// * `version_sync_file_location: Option` - The location for the version sync file. This is optional and may be `None`. +/// * `github_mirror: Option` - The GitHub mirror to use. This is optional and may be `None`. +/// * `rollback_limit: Option` - The rollback limit. This is optional and may be `None`. +/// +/// # Example +/// +/// ```rust +/// let config = Config { +/// enable_nightly_info: Some(true), +/// enable_release_build: Some(false), +/// downloads_location: Some("/path/to/downloads".to_string()), +/// installation_location: Some("/path/to/installation".to_string()), +/// version_sync_file_location: Some("/path/to/version_sync_file".to_string()), +/// github_mirror: Some("https://github.com".to_string()), +/// rollback_limit: Some(5), +/// }; +/// println!("The configuration is {:?}", config); +/// ``` #[derive(Serialize, Deserialize, Debug)] pub struct Config { pub enable_nightly_info: Option, @@ -15,6 +43,20 @@ pub struct Config { pub rollback_limit: Option, } +/// Handles the application configuration. +/// +/// This function reads the configuration file, which can be in either TOML or JSON format, and returns a `Config` object. If the configuration file does not exist, it returns a `Config` object with all fields set to `None`. +/// +/// # Returns +/// +/// * `Result` - Returns a `Result` that contains a `Config` object if the function completes successfully. If an error occurs, it returns `Err`. +/// +/// # Example +/// +/// ```rust +/// let config = handle_config().await.unwrap(); +/// println!("The configuration is {:?}", config); +/// ``` pub async fn handle_config() -> Result { let config_file = crate::helpers::directories::get_config_file()?; let config = match fs::read_to_string(&config_file).await { @@ -43,6 +85,33 @@ pub async fn handle_config() -> Result { Ok(config) } +/// Handles environment variables in the configuration. +/// +/// This function takes a mutable reference to a `Config` object. It creates a `Regex` to match environment variables in the format `$VARIABLE_NAME`. It then calls the `handle_envar` function for each field in the `Config` object that may contain an environment variable. +/// +/// # Arguments +/// +/// * `config: &mut Config` - A mutable reference to a `Config` object that may contain environment variables. +/// +/// # Returns +/// +/// * `Result<()>` - Returns `Ok(())` if the function completes successfully. If an error occurs, it returns `Err`. +/// +/// # Example +/// +/// ```rust +/// let mut config = Config { +/// downloads_location: Some("DOWNLOADS=${DOWNLOADS}".to_string()), +/// github_mirror: Some("GITHUB=${GITHUB}".to_string()), +/// installation_location: Some("INSTALL=${INSTALL}".to_string()), +/// version_sync_file_location: Some("SYNC=${SYNC}".to_string()), +/// }; +/// handle_envars(&mut config).unwrap(); +/// assert_eq!(config.downloads_location, Some(format!("DOWNLOADS={}", env::var("DOWNLOADS").unwrap()))); +/// assert_eq!(config.github_mirror, Some(format!("GITHUB={}", env::var("GITHUB").unwrap()))); +/// assert_eq!(config.installation_location, Some(format!("INSTALL={}", env::var("INSTALL").unwrap()))); +/// assert_eq!(config.version_sync_file_location, Some(format!("SYNC={}", env::var("SYNC").unwrap()))); +/// ``` fn handle_envars(config: &mut Config) -> Result<()> { let re = Regex::new(r"\$([A-Z_]+)").unwrap(); @@ -57,6 +126,27 @@ fn handle_envars(config: &mut Config) -> Result<()> { Ok(()) } +/// Handles environment variables in the configuration. +/// +/// This function takes a mutable reference to an `Option` and a reference to a `Regex`. If the `Option` is `None`, the function returns `Ok(())`. If the `Option` is `Some(value)`, the function checks if the `value` matches the `Regex`. If it does, the function extracts the environment variable from the `value`, replaces the environment variable in the `value` with its value from the environment, and updates the `Option` with the new `value`. +/// +/// # Arguments +/// +/// * `item: &mut Option` - A mutable reference to an `Option` that may contain an environment variable. +/// * `re: &Regex` - A reference to a `Regex` to match the environment variable in the `Option`. +/// +/// # Returns +/// +/// * `Result<()>` - Returns `Ok(())` if the function completes successfully. If an error occurs, it returns `Err`. +/// +/// # Example +/// +/// ```rust +/// let mut item = Some("HOME=${HOME}".to_string()); +/// let re = Regex::new(r"\$\{(.+?)\}").unwrap(); +/// handle_envar(&mut item, &re).unwrap(); +/// assert_eq!(item, Some(format!("HOME={}", env::var("HOME").unwrap()))); +/// ``` fn handle_envar(item: &mut Option, re: &Regex) -> Result<()> { let value = if let Some(value) = item.as_ref() { value diff --git a/src/github_requests.rs b/src/github_requests.rs index 7a1c47d..cd860e2 100644 --- a/src/github_requests.rs +++ b/src/github_requests.rs @@ -3,6 +3,28 @@ use chrono::{DateTime, Utc}; use reqwest::Client; use serde::{de::DeserializeOwned, Deserialize, Serialize}; +/// Represents the version of the upstream software in the GitHub API. +/// +/// This struct contains the tag name of the version, the target commitish of the version, and the date and time the version was published. +/// +/// # Fields +/// +/// * `tag_name: String` - The tag name of the version. +/// * `target_commitish: Option` - The target commitish of the version. This is optional and may be `None`. +/// * `published_at: DateTime` - The date and time the version was published, represented as a `DateTime` object. +/// +/// # Example +/// +/// ```rust +/// let upstream_version = UpstreamVersion { +/// tag_name: "v1.0.0".to_string(), +/// target_commitish: Some("abc123".to_string()), +/// published_at: Utc::now(), +/// }; +/// println!("The tag name is {}", upstream_version.tag_name); +/// println!("The target commitish is {}", upstream_version.target_commitish.unwrap_or_default()); +/// println!("The published date and time is {}", upstream_version.published_at); +/// ``` #[derive(Serialize, Deserialize, Debug, Clone)] pub struct UpstreamVersion { pub tag_name: String, @@ -10,29 +32,135 @@ pub struct UpstreamVersion { pub published_at: DateTime, } +/// Represents a repository commit in the GitHub API. +/// +/// This struct contains the SHA of a commit and the commit details, as returned by the GitHub API. +/// +/// # Fields +/// +/// * `sha: String` - The SHA of the commit. +/// * `commit: Commit` - The details of the commit, represented as a `Commit` object. +/// +/// # Example +/// +/// ```rust +/// let commit_author = CommitAuthor { +/// name: "Alice".to_string(), +/// }; +/// let commit = Commit { +/// author: commit_author, +/// message: "Initial commit".to_string(), +/// }; +/// let repo_commit = RepoCommit { +/// sha: "abc123".to_string(), +/// commit: commit, +/// }; +/// println!("The commit SHA is {}", repo_commit.sha); +/// println!("The commit author is {}", repo_commit.commit.author.name); +/// println!("The commit message is {}", repo_commit.commit.message); +/// ``` #[derive(Serialize, Deserialize, Debug)] pub struct RepoCommit { pub sha: String, pub commit: Commit, } +/// Represents a commit in the GitHub API. +/// +/// This struct contains the author of a commit and the commit message, as returned by the GitHub API. +/// +/// # Fields +/// +/// * `author: CommitAuthor` - The author of the commit, represented as a `CommitAuthor` object. +/// * `message: String` - The commit message. +/// +/// # Example +/// +/// ```rust +/// let commit_author = CommitAuthor { +/// name: "Alice".to_string(), +/// }; +/// let commit = Commit { +/// author: commit_author, +/// message: "Initial commit".to_string(), +/// }; +/// println!("The commit author is {}", commit.author.name); +/// println!("The commit message is {}", commit.message); +/// ``` #[derive(Serialize, Deserialize, Debug)] pub struct Commit { pub author: CommitAuthor, pub message: String, } +/// Represents the author of a commit in the GitHub API. +/// +/// This struct contains the name of the author of a commit, as returned by the GitHub API. +/// +/// # Fields +/// +/// * `name: String` - The name of the author of the commit. +/// +/// # Example +/// +/// ```rust +/// let commit_author = CommitAuthor { +/// name: "Alice".to_string(), +/// }; +/// println!("The commit author is {}", commit_author.name); +/// ``` #[derive(Serialize, Deserialize, Debug)] pub struct CommitAuthor { pub name: String, } +/// Represents an error response from the GitHub API. +/// +/// This struct contains information about an error response from the GitHub API, including the error message and the URL of the documentation related to the error. +/// +/// # Fields +/// +/// * `message: String` - The error message from the GitHub API. +/// * `documentation_url: String` - The URL of the documentation related to the error. +/// +/// # Example +/// +/// ```rust +/// let error_response = ErrorResponse { +/// message: "Not Found".to_string(), +/// documentation_url: "https://docs.github.com/rest".to_string(), +/// }; +/// println!("The error message is {}", error_response.message); +/// println!("The documentation URL is {}", error_response.documentation_url); +/// ``` #[derive(Debug, Deserialize, Serialize)] pub struct ErrorResponse { pub message: String, pub documentation_url: String, } +/// Fetches the upstream nightly version from the GitHub API. +/// +/// This function sends a GET request to the GitHub API to fetch the upstream nightly version of the software. The request is sent to the URL "https://api.github.com/repos/neovim/neovim/releases/tags/nightly". +/// +/// # Parameters +/// +/// * `client: &Client` - The HTTP client used to send the request. +/// +/// # Returns +/// +/// * `Result` - The upstream nightly version as an `UpstreamVersion` object, or an error if the request failed. +/// +/// # Example +/// +/// ```rust +/// let client = Client::new(); +/// let result = get_upstream_nightly(&client).await; +/// match result { +/// Ok(version) => println!("Received version: {:?}", version), +/// Err(e) => println!("An error occurred: {:?}", e), +/// } +/// ``` pub async fn get_upstream_nightly(client: &Client) -> Result { let response = client .get("https://api.github.com/repos/neovim/neovim/releases/tags/nightly") @@ -46,6 +174,32 @@ pub async fn get_upstream_nightly(client: &Client) -> Result { deserialize_response(response) } +/// Fetches the commits for the nightly version from the GitHub API. +/// +/// This function sends a GET request to the GitHub API to fetch the commits for the nightly version of the software. The commits are fetched for a specified time range, from `since` to `until`. +/// +/// # Parameters +/// +/// * `client: &Client` - The HTTP client used to send the request. +/// * `since: &DateTime` - The start of the time range for which to fetch the commits. +/// * `until: &DateTime` - The end of the time range for which to fetch the commits. +/// +/// # Returns +/// +/// * `Result>` - A vector of `RepoCommit` objects representing the commits for the nightly version, or an error if the request failed. +/// +/// # Example +/// +/// ```rust +/// let client = Client::new(); +/// let since = Utc::now() - Duration::days(1); +/// let until = Utc::now(); +/// let result = get_commits_for_nightly(&client, &since, &until).await; +/// match result { +/// Ok(commits) => println!("Received {} commits", commits.len()), +/// Err(e) => println!("An error occurred: {:?}", e), +/// } +/// ``` pub async fn get_commits_for_nightly( client: &Client, since: &DateTime, @@ -64,6 +218,32 @@ pub async fn get_commits_for_nightly( deserialize_response(response) } +/// Deserializes a JSON response from the GitHub API. +/// +/// This function takes a JSON response as a string and attempts to deserialize it into a specified type `T`. If the response contains a "message" field, it is treated as an error response and the function will return an error with the message from the response. If the error is related to rate limiting, a specific error message is returned. +/// +/// # Parameters +/// +/// * `response: String` - The JSON response from the GitHub API as a string. +/// +/// # Returns +/// +/// * `Result` - The deserialized response as the specified type `T`, or an error if the response could not be deserialized or contains an error message. +/// +/// # Errors +/// +/// This function will return an error if the response contains a "message" field (indicating an error from the GitHub API), or if the response could not be deserialized into the specified type `T`. +/// +/// # Example +/// +/// ```rust +/// let response = "{\"data\": \"some data\"}"; +/// let result: Result = deserialize_response(response); +/// match result { +/// Ok(data) => println!("Received data: {:?}", data), +/// Err(e) => println!("An error occurred: {:?}", e), +/// } +/// ``` pub fn deserialize_response(response: String) -> Result { let value: serde_json::Value = serde_json::from_str(&response)?; diff --git a/src/handlers/erase_handler.rs b/src/handlers/erase_handler.rs index 7eeedfc..4b25e0e 100644 --- a/src/handlers/erase_handler.rs +++ b/src/handlers/erase_handler.rs @@ -4,6 +4,41 @@ use tracing::info; use crate::{config::Config, helpers::directories}; +/// Starts the erase process based on the provided `Config`. +/// +/// # Arguments +/// +/// * `config: Config` - Contains the configuration settings. +/// +/// # Behavior +/// +/// The function attempts to remove the installation and downloads directories. If successful, it logs a success message. If the directories do not exist, it returns an error. +/// +/// On Windows, it also attempts to remove the Neovim installation path from the registry. If successful, it logs a success message. +/// +/// # Returns +/// +/// * `Result<()>` - Returns `Ok(())` if the function executes successfully, otherwise it returns an error. +/// +/// # Errors +/// +/// This function will return an error if there's a problem with removing the directories or modifying the registry. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// start(config).await?; +/// ``` +/// +/// # Note +/// +/// This function is asynchronous and must be awaited. +/// +/// # See Also +/// +/// * [`directories::get_downloads_directory`](src/helpers/directories.rs) +/// * [`directories::get_installation_directory`](src/helpers/directories.rs) pub async fn start(config: Config) -> Result<()> { let downloads = directories::get_downloads_directory(&config).await?; let installation_dir = directories::get_installation_directory(&config).await?; diff --git a/src/handlers/install_handler.rs b/src/handlers/install_handler.rs index e2102b8..6278b9a 100644 --- a/src/handlers/install_handler.rs +++ b/src/handlers/install_handler.rs @@ -22,6 +22,45 @@ use yansi::Paint; use super::{InstallResult, PostDownloadVersionType}; +/// Starts the installation process for a given version. +/// +/// # Arguments +/// +/// * `version` - A mutable reference to a `ParsedVersion` object representing the version to be installed. +/// * `client` - A reference to a `Client` object used for making HTTP requests. +/// * `config` - A reference to a `Config` object containing the configuration settings. +/// +/// # Returns +/// +/// * `Result` - Returns a `Result` that contains an `InstallResult` enum on success, or an error on failure. +/// +/// # Errors +/// +/// This function will return an error if: +/// * The version is below 0.2.2. +/// * There is a problem setting the current directory. +/// * There is a problem checking if the version is already installed. +/// * There is a problem getting the upstream nightly version. +/// * There is a problem getting the local nightly version. +/// * There is a problem handling a rollback. +/// * There is a problem printing commits. +/// * There is a problem downloading the version. +/// * There is a problem handling building from source. +/// * There is a problem unarchiving the downloaded file. +/// * There is a problem creating the file `nightly/bob.json`. +/// +/// # Panics +/// +/// This function does not panic. +/// +/// # Examples +/// +/// ```rust +/// let mut version = ParsedVersion::new(VersionType::Normal, "1.0.0"); +/// let client = Client::new(); +/// let config = Config::default(); +/// let result = start(&mut version, &client, &config).await; +/// ``` pub async fn start( version: &mut ParsedVersion, client: &Client, @@ -117,6 +156,37 @@ pub async fn start( )) } +/// Asynchronously handles the rollback of the nightly version of Neovim. +/// +/// This function checks if the nightly version is used and if the rollback limit is not zero. +/// If these conditions are met, it produces a vector of nightly versions and removes the oldest version if the vector's length is greater than or equal to the rollback limit. +/// It also handles permissions for older installations of nightly on Unix platforms. +/// Finally, it creates a rollback by copying the nightly directory to a new directory with the ID of the target commitish and updates the JSON file in the new directory. +/// +/// # Arguments +/// +/// * `config` - A reference to the configuration object. +/// +/// # Returns +/// +/// * `Result<()>` - Returns a `Result` that contains `()` on success, or an error on failure. +/// +/// # Errors +/// +/// This function will return an error if: +/// * There is a failure in producing the vector of nightly versions. +/// * There is a failure in removing the oldest version. +/// * There is a failure in reading the nightly JSON file. +/// * There is a failure in parsing the JSON file. +/// * There is a failure in copying the nightly directory. +/// * There is a failure in writing the updated JSON file. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// handle_rollback(&config).await?; +/// ` async fn handle_rollback(config: &Config) -> Result<()> { if !helpers::version::is_version_used("nightly", config).await { return Ok(()); @@ -174,6 +244,34 @@ async fn handle_rollback(config: &Config) -> Result<()> { Ok(()) } +/// Asynchronously prints the commits between two versions of Neovim. +/// +/// This function fetches the commits between the published dates of the local and upstream versions of Neovim. +/// It then prints each commit with the author's name in blue and the commit message. +/// +/// # Arguments +/// +/// * `client` - A reference to the HTTP client. +/// * `local` - A reference to the local version of Neovim. +/// * `upstream` - A reference to the upstream version of Neovim. +/// +/// # Returns +/// +/// * `Result<()>` - Returns a `Result` that contains `()` on success, or an error on failure. +/// +/// # Errors +/// +/// This function will return an error if: +/// * There is a failure in fetching the commits between the published dates of the local and upstream versions. +/// +/// # Example +/// +/// ```rust +/// let client = Client::new(); +/// let local = UpstreamVersion::get_local_version(); +/// let upstream = UpstreamVersion::get_upstream_version(&client).await?; +/// print_commits(&client, &local, &upstream).await?; +/// ``` async fn print_commits( client: &Client, local: &UpstreamVersion, @@ -193,6 +291,41 @@ async fn print_commits( Ok(()) } +/// Asynchronously downloads a specified version of Neovim. +/// +/// This function sends a request to download the specified version of Neovim based on the version type. +/// If the version type is Normal, Nightly, or Latest, it sends a request to download the version. +/// If the version type is Hash, it handles building from source. +/// If the version type is NightlyRollback, it does nothing. +/// +/// # Arguments +/// +/// * `client` - A reference to the HTTP client. +/// * `version` - A reference to the parsed version of Neovim to be downloaded. +/// * `root` - A reference to the path where the downloaded file will be saved. +/// * `config` - A reference to the configuration object. +/// +/// # Returns +/// +/// * `Result` - Returns a `Result` that contains a `PostDownloadVersionType` on success, or an error on failure. +/// +/// # Errors +/// +/// This function will return an error if: +/// * There is a failure in sending the request to download the version. +/// * The response status is not 200. +/// * There is a failure in creating the file where the downloaded version will be saved. +/// * There is a failure in writing the downloaded bytes to the file. +/// +/// # Example +/// +/// ```rust +/// let client = Client::new(); +/// let version = ParsedVersion::parse("0.5.0"); +/// let root = Path::new("/path/to/save"); +/// let config = Config::default(); +/// let result = download_version(&client, &version, &root, &config).await; +/// ``` async fn download_version( client: &Client, version: &ParsedVersion, @@ -259,6 +392,41 @@ async fn download_version( } } +/// Asynchronously handles the building of a specified version from source. +/// +/// This function checks for the presence of necessary tools (like Clang, GCC, Cmake, and Git) in the system. +/// It then proceeds to create a directory named "neovim-git" if it doesn't exist, and sets the current directory to it. +/// It initializes a Git repository if one doesn't exist, and sets the remote to Neovim's GitHub repository. +/// It fetches the specified version from the remote repository and checks out the fetched files. +/// It then builds the fetched files and installs them to a specified location. +/// +/// # Arguments +/// +/// * `version` - A reference to the parsed version of Neovim to be built. +/// * `config` - A reference to the configuration object. +/// +/// # Returns +/// +/// * `Result` - Returns a `Result` that contains a `PostDownloadVersionType` on success, or an error on failure. +/// +/// # Errors +/// +/// This function will return an error if: +/// * The necessary tools are not installed in the system. +/// * There is a failure in creating the "neovim-git" directory. +/// * There is a failure in initializing the Git repository. +/// * There is a failure in setting the remote repository. +/// * There is a failure in fetching the specified version from the remote repository. +/// * There is a failure in checking out the fetched files. +/// * There is a failure in building and installing the fetched files. +/// +/// # Example +/// +/// ```rust +/// let version = ParsedVersion::parse("0.5.0"); +/// let config = Config::default(); +/// let result = handle_building_from_source(&version, &config).await; +/// ``` async fn handle_building_from_source( version: &ParsedVersion, config: &Config, @@ -432,6 +600,41 @@ async fn handle_building_from_source( Ok(PostDownloadVersionType::Hash) } +/// Sends a GET request to the specified URL to download a specific version of Neovim. +/// +/// # Arguments +/// +/// * `client: &Client` - A reference to the `Client` used for making requests. +/// * `config: &Config` - Contains the configuration settings. +/// * `version: &ParsedVersion` - Contains the version information to be downloaded. +/// +/// # Behavior +/// +/// The function constructs the download URL based on the provided `version` and `config.github_mirror`. If `config.github_mirror` is `None`, it defaults to "https://github.com". +/// +/// It then sends a GET request to the constructed URL with the header "user-agent" set to "bob". +/// +/// # Returns +/// +/// * `Result` - Returns a `Result` containing the server's response to the GET request. If the request fails, it returns an error. +/// +/// # Example +/// +/// ```rust +/// let client = Client::new(); +/// let config = Config::default(); +/// let version = ParsedVersion { tag_name: "v0.2.2", semver: Version::parse("0.2.2").unwrap() }; +/// let response = send_request(&client, &config, &version).await?; +/// ``` +/// +/// # Note +/// +/// This function is asynchronous and must be awaited. +/// +/// # See Also +/// +/// * [`helpers::get_platform_name_download`](src/helpers/platform.rs) +/// * [`helpers::get_file_type`](src/helpers/file.rs) async fn send_request( client: &Client, config: &Config, diff --git a/src/handlers/list_handler.rs b/src/handlers/list_handler.rs index 82b55da..30a39f1 100644 --- a/src/handlers/list_handler.rs +++ b/src/handlers/list_handler.rs @@ -8,6 +8,25 @@ use crate::{ helpers::{self, directories, version::nightly::produce_nightly_vec}, }; +/// Starts the list handler. +/// +/// This function reads the downloads directory and lists all the installed versions in a formatted table. It also checks if a version is currently in use. +/// +/// # Arguments +/// +/// * `config` - The configuration object. +/// +/// # Returns +/// +/// * `Result<()>` - Returns `Ok(())` if the operation is successful, or an error if there are no versions installed or if there is a failure in reading the directory or checking if a version is in use. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// let result = start(config).await; +/// assert!(result.is_ok()); +/// ``` pub async fn start(config: Config) -> Result<()> { let downloads_dir = directories::get_downloads_directory(&config).await?; @@ -86,12 +105,50 @@ pub async fn start(config: Config) -> Result<()> { Ok(()) } +/// Checks if there are any rollbacks available. +/// +/// This function produces a vector of nightly versions and checks if it is empty. +/// +/// # Arguments +/// +/// * `config` - A reference to the configuration object. +/// +/// # Returns +/// +/// * `Result` - Returns a `Result` that contains `true` if there are rollbacks available, or `false` otherwise. Returns an error if there is a failure in producing the vector of nightly versions. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// let has_rollbacks = has_rollbacks(&config).await?; +/// assert_eq!(has_rollbacks, true); +/// ``` async fn has_rollbacks(config: &Config) -> Result { let list = produce_nightly_vec(config).await?; Ok(!list.is_empty()) } +/// Checks if a given string is a valid version. +/// +/// This function checks if the given string is "stable", contains "nightly", or matches the version or hash regex. +/// +/// # Arguments +/// +/// * `name` - A reference to a string that could be a version. +/// +/// # Returns +/// +/// * `bool` - Returns `true` if the string is a valid version, `false` otherwise. +/// +/// # Example +/// +/// ```rust +/// let version = "v1.0.0"; +/// let is_version = is_version(version); +/// assert_eq!(is_version, true); +/// ``` fn is_version(name: &str) -> bool { match name { "stable" => true, diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index 4e49f56..6ba9437 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -9,6 +9,14 @@ pub mod use_handler; use super::version::types::LocalVersion; +/// Represents the result of an installation attempt. +/// +/// This enum has four variants: +/// +/// * `InstallationSuccess(String)` - The installation was successful. +/// * `VersionAlreadyInstalled` - The version that was attempted to be installed is already installed. +/// * `NightlyIsUpdated` - The nightly version is updated. +/// * `GivenNightlyRollback` - The given nightly version is a rollback. pub enum InstallResult { InstallationSuccess(String), VersionAlreadyInstalled, @@ -16,6 +24,13 @@ pub enum InstallResult { GivenNightlyRollback, } +/// Represents the type of a version after it has been downloaded. +/// +/// This enum has three variants: +/// +/// * `None` - No specific version type is assigned. +/// * `Standard(LocalVersion)` - The version is a standard version. The `LocalVersion` contains the details of the version. +/// * `Hash` - The version is identified by a hash. #[derive(PartialEq)] pub enum PostDownloadVersionType { None, diff --git a/src/handlers/rollback_handler.rs b/src/handlers/rollback_handler.rs index aaeab37..9513c9e 100644 --- a/src/handlers/rollback_handler.rs +++ b/src/handlers/rollback_handler.rs @@ -10,6 +10,24 @@ use crate::{ helpers::{self, version::types::ParsedVersion}, }; +/// Starts the rollback process. +/// +/// This function presents a list of available versions to the user, allows them to select a version to rollback to, and then performs the rollback. +/// +/// # Arguments +/// +/// * `config` - The configuration for the rollback process. +/// +/// # Returns +/// +/// * `Result<()>` - Returns a `Result` that indicates whether the rollback process was successful or not. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// start(config).await.unwrap(); +/// ``` pub async fn start(config: Config) -> Result<()> { let nightly_vec = produce_nightly_vec(&config).await?; @@ -80,6 +98,25 @@ pub async fn start(config: Config) -> Result<()> { Ok(()) } +/// Converts a `Duration` into a human-readable string. +/// +/// This function takes a `Duration` and converts it into a string that represents the duration in weeks, days, and hours. +/// +/// # Arguments +/// +/// * `duration` - The `Duration` to be converted. +/// +/// # Returns +/// +/// * `Result` - Returns a `Result` that contains a string representing the duration in a human-readable format, or an error if there is a failure in the conversion process. +/// +/// # Example +/// +/// ```rust +/// let duration = Duration::hours(25); +/// let humanized_duration = humanize_duration(duration).unwrap(); +/// assert_eq!(humanized_duration, "1 day, 1 hour"); +/// ``` fn humanize_duration(duration: Duration) -> Result { let mut humanized_duration = String::new(); diff --git a/src/handlers/sync_handler.rs b/src/handlers/sync_handler.rs index 7793d42..52fca1e 100644 --- a/src/handlers/sync_handler.rs +++ b/src/handlers/sync_handler.rs @@ -7,6 +7,34 @@ use crate::{config::Config, helpers::version}; use super::use_handler; +/// Starts the synchronization process. +/// +/// This function reads the version from a sync file and starts the use handler with the read version. +/// +/// # Arguments +/// +/// * `client` - The HTTP client to be used for network requests. +/// * `config` - The configuration for the synchronization process. +/// +/// # Returns +/// +/// * `Result<()>` - Returns a `Result` that indicates whether the synchronization process was successful or not. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The `version_sync_file_location` is not set in the configuration. +/// * The sync file is empty. +/// * The version read from the sync file contains "nightly-". +/// +/// # Example +/// +/// ```rust +/// let client = Client::new(); +/// let config = Config::default(); +/// start(&client, config).await.unwrap(); +/// ``` pub async fn start(client: &Client, config: Config) -> Result<()> { let version_sync_file_location = version::get_version_sync_file_location(&config) .await? diff --git a/src/handlers/uninstall_handler.rs b/src/handlers/uninstall_handler.rs index 84843f5..3661b2a 100644 --- a/src/handlers/uninstall_handler.rs +++ b/src/handlers/uninstall_handler.rs @@ -13,6 +13,34 @@ use crate::{ helpers::{self, directories}, }; +/// Starts the uninstall process. +/// +/// This function creates a new HTTP client, determines the version to uninstall, checks if the version is currently in use, and if not, removes the version's directory. +/// +/// # Arguments +/// +/// * `version` - An optional string that represents the version to uninstall. If `None`, the function will call `uninstall_selections` to allow the user to select versions to uninstall. +/// * `config` - The configuration for the uninstall process. +/// +/// # Returns +/// +/// * `Result<()>` - Returns a `Result` that indicates whether the uninstall process was successful or not. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The version cannot be parsed. +/// * The version is currently in use. +/// * The downloads directory cannot be determined. +/// * The version's directory cannot be removed. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// start(Some("1.0.0"), config).await.unwrap(); +/// ``` pub async fn start(version: Option<&str>, config: Config) -> Result<()> { let client = Client::new(); @@ -42,6 +70,35 @@ pub async fn start(version: Option<&str>, config: Config) -> Result<()> { Ok(()) } +/// Uninstalls selected versions. +/// +/// This function reads the versions from the downloads directory, presents a list of installed versions to the user, allows them to select versions to uninstall, and then uninstalls the selected versions. +/// +/// # Arguments +/// +/// * `client` - The HTTP client to be used for network requests. +/// * `config` - The configuration for the uninstall process. +/// +/// # Returns +/// +/// * `Result<()>` - Returns a `Result` that indicates whether the uninstall process was successful or not. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The downloads directory cannot be read. +/// * The version cannot be parsed from the file name. +/// * The version is currently in use. +/// * The user aborts the uninstall process. +/// +/// # Example +/// +/// ```rust +/// let client = Client::new(); +/// let config = Config::default(); +/// uninstall_selections(&client, &config).await.unwrap(); +/// ``` async fn uninstall_selections(client: &Client, config: &Config) -> Result<()> { let downloads_dir = directories::get_downloads_directory(config).await?; diff --git a/src/handlers/update_handler.rs b/src/handlers/update_handler.rs index c6bd167..f8e0966 100644 --- a/src/handlers/update_handler.rs +++ b/src/handlers/update_handler.rs @@ -6,6 +6,48 @@ use tracing::{info, warn}; use super::{install_handler, InstallResult}; +/// Starts the update process based on the provided `Update` data, `Client`, and `Config`. +/// +/// # Arguments +/// +/// * `data: Update` - Contains the version information to be updated. If `data.version` is `None` or `data.all` is `true`, it will attempt to update all installed versions. +/// * `client: &Client` - A reference to the `Client` used for making requests. +/// * `config: Config` - Contains the configuration settings. +/// +/// # Behavior +/// +/// If `data.version` is `None` or `data.all` is `true`, the function will attempt to update both the "stable" and "nightly" versions if they are installed. If an update is successful, `did_update` is set to `true`. +/// +/// If neither version is updated, a warning message "There was nothing to update." is logged. +/// +/// If `data.version` is not `None` and `data.all` is not `true`, the function will attempt to update the specified version if it is installed. If the version is not installed, a warning message is logged. +/// +/// # Returns +/// +/// * `Result<()>` - Returns `Ok(())` if the function executes successfully, otherwise it returns an error. +/// +/// # Errors +/// +/// This function will return an error if there's a problem with parsing the version type, checking if a version is installed, or starting the installation process. +/// +/// # Example +/// +/// ```rust +/// let data = Update { version: Some("0.2.2"), all: false }; +/// let client = Client::new(); +/// let config = Config::default(); +/// start(data, &client, config).await?; +/// ``` +/// +/// # Note +/// +/// This function is asynchronous and must be awaited. +/// +/// # See Also +/// +/// * [`crate::version::parse_version_type`](src/version.rs) +/// * [`is_version_installed`](src/helpers/version.rs) +/// * [`install_handler::start`](src/handlers/install_handler.rs) pub async fn start(data: Update, client: &Client, config: Config) -> Result<()> { if data.all { let mut did_update = false; diff --git a/src/handlers/use_handler.rs b/src/handlers/use_handler.rs index 2fa3976..6ec6a28 100644 --- a/src/handlers/use_handler.rs +++ b/src/handlers/use_handler.rs @@ -11,6 +11,38 @@ use crate::handlers::{install_handler, InstallResult}; use crate::helpers; use crate::helpers::version::types::{ParsedVersion, VersionType}; +/// Starts the process of using a specified version. +/// +/// This function checks if the specified version is already used, copies the Neovim proxy to the installation directory, installs the version if it's not already installed and used, switches to the version, and removes the "stable" directory if the version type is "Latest". +/// +/// # Arguments +/// +/// * `version` - The version to use. +/// * `install` - Whether to install the version if it's not already installed. +/// * `client` - The client to use for HTTP requests. +/// * `config` - The configuration for the operation. +/// +/// # Returns +/// +/// * `Result<()>` - Returns a `Result` that indicates whether the operation was successful or not. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The version is not already used and it cannot be installed. +/// * The version cannot be switched to. +/// * The "stable" directory exists and it cannot be removed. +/// +/// # Example +/// +/// ```rust +/// let version = ParsedVersion::new("1.0.0"); +/// let install = true; +/// let client = Client::new(); +/// let config = Config::default(); +/// start(version, install, &client, config).await.unwrap(); +/// ``` pub async fn start( mut version: ParsedVersion, install: bool, @@ -52,6 +84,36 @@ pub async fn start( Ok(()) } +/// Switches to a specified version. +/// +/// This function changes the current directory to the downloads directory, writes the version to a file named "used", and if the version is different from the version stored in `version_sync_file_location`, it also writes the version to `version_sync_file_location`. +/// +/// # Arguments +/// +/// * `config` - The configuration for the operation. +/// * `version` - The version to switch to. +/// +/// # Returns +/// +/// * `Result<()>` - Returns a `Result` that indicates whether the operation was successful or not. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The downloads directory cannot be determined. +/// * The current directory cannot be changed to the downloads directory. +/// * The version cannot be written to the "used" file. +/// * The version cannot be read from `version_sync_file_location`. +/// * The version cannot be written to `version_sync_file_location`. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// let version = ParsedVersion::new("1.0.0"); +/// switch(&config, &version).await.unwrap(); +/// ``` pub async fn switch(config: &Config, version: &ParsedVersion) -> Result<()> { std::env::set_current_dir(helpers::directories::get_downloads_directory(config).await?)?; @@ -76,6 +138,36 @@ pub async fn switch(config: &Config, version: &ParsedVersion) -> Result<()> { Ok(()) } +/// Copies the Neovim proxy to the installation directory. +/// +/// This function gets the current executable's path, determines the installation directory, creates it if it doesn't exist, adds it to the system's PATH, and copies the current executable to the installation directory as "nvim" or "nvim.exe" (on Windows). +/// +/// If a file named "nvim" or "nvim.exe" already exists in the installation directory, the function checks its version. If the version matches the current version, the function does nothing. Otherwise, it replaces the file with the current executable. +/// +/// # Arguments +/// +/// * `config` - The configuration for the operation. +/// +/// # Returns +/// +/// * `Result<()>` - Returns a `Result` that indicates whether the operation was successful or not. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The current executable's path cannot be determined. +/// * The installation directory cannot be created. +/// * The installation directory cannot be added to the PATH. +/// * The version of the existing file cannot be determined. +/// * The existing file cannot be replaced. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// copy_nvim_proxy(&config).await.unwrap(); +/// ``` async fn copy_nvim_proxy(config: &Config) -> Result<()> { let exe_path = env::current_exe().unwrap(); let mut installation_dir = helpers::directories::get_installation_directory(config).await?; @@ -107,6 +199,32 @@ async fn copy_nvim_proxy(config: &Config) -> Result<()> { Ok(()) } +/// Adds the installation directory to the system's PATH. +/// +/// This function checks if the installation directory is already in the PATH. If not, it adds the directory to the PATH. +/// +/// # Arguments +/// +/// * `installation_dir` - The directory to be added to the PATH. +/// +/// # Returns +/// +/// * `Result<()>` - Returns a `Result` that indicates whether the operation was successful or not. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The installation directory cannot be converted to a string. +/// * The current user's environment variables cannot be accessed or modified (Windows only). +/// * The PATH environment variable cannot be read (non-Windows only). +/// +/// # Example +/// +/// ```rust +/// let installation_dir = Path::new("/usr/local/bin"); +/// add_to_path(&installation_dir).unwrap(); +/// ``` fn add_to_path(installation_dir: &Path) -> Result<()> { let installation_dir = installation_dir.to_str().unwrap(); diff --git a/src/helpers/directories.rs b/src/helpers/directories.rs index d242801..69b5eaa 100644 --- a/src/helpers/directories.rs +++ b/src/helpers/directories.rs @@ -4,6 +4,24 @@ use std::path::PathBuf; use crate::config::Config; +/// Returns the home directory path for the current user. +/// +/// This function checks the target operating system using the `cfg!` macro and constructs the home directory path accordingly. +/// For Windows, it uses the "USERPROFILE" environment variable. +/// For macOS, it uses the "/Users/" directory and appends the "SUDO_USER" or "USER" environment variable if they exist and correspond to a valid directory. +/// For other operating systems, it uses the "/home/" directory and appends the "SUDO_USER" or "USER" environment variable if they exist and correspond to a valid directory. +/// If none of the above methods work, it uses the "HOME" environment variable. +/// +/// # Returns +/// +/// This function returns a `Result` that contains a `PathBuf` representing the home directory path if the operation was successful. +/// If the operation failed, the function returns `Err` with a description of the error. +/// +/// # Example +/// +/// ```rust +/// let home_dir = get_home_dir()?; +/// ``` pub fn get_home_dir() -> Result { let mut home_str = PathBuf::new(); @@ -38,6 +56,23 @@ pub fn get_home_dir() -> Result { Ok(home_str) } +/// Returns the local data directory path for the current user. +/// +/// This function first gets the home directory path by calling the `get_home_dir` function. +/// It then checks the target operating system using the `cfg!` macro and constructs the local data directory path accordingly. +/// For Windows, it appends "AppData/Local" to the home directory path. +/// For other operating systems, it appends ".local/share" to the home directory path. +/// +/// # Returns +/// +/// This function returns a `Result` that contains a `PathBuf` representing the local data directory path if the operation was successful. +/// If the operation failed, the function returns `Err` with a description of the error. +/// +/// # Example +/// +/// ```rust +/// let local_data_dir = get_local_data_dir()?; +/// ``` pub fn get_local_data_dir() -> Result { let mut home_dir = get_home_dir()?; if cfg!(windows) { @@ -49,6 +84,23 @@ pub fn get_local_data_dir() -> Result { Ok(home_dir) } +/// Returns the local data directory path for the current user. +/// +/// This function first gets the home directory path by calling the `get_home_dir` function. +/// It then checks the target operating system using the `cfg!` macro and constructs the local data directory path accordingly. +/// For Windows, it appends "AppData/Local" to the home directory path. +/// For other operating systems, it appends ".local/share" to the home directory path. +/// +/// # Returns +/// +/// This function returns a `Result` that contains a `PathBuf` representing the local data directory path if the operation was successful. +/// If the operation failed, the function returns `Err` with a description of the error. +/// +/// # Example +/// +/// ```rust +/// let local_data_dir = get_local_data_dir()?; +/// ``` pub fn get_config_file() -> Result { if let Ok(value) = std::env::var("BOB_CONFIG") { return Ok(PathBuf::from(value)); @@ -74,6 +126,28 @@ pub fn get_config_file() -> Result { Ok(home_dir) } +/// Asynchronously returns the downloads directory path based on the application configuration. +/// +/// This function takes a reference to a `Config` as an argument, which contains the application configuration. +/// It first checks if the `downloads_location` field in the `Config` is set. If it is, it checks if the directory exists. If the directory does not exist, it returns an error. +/// If the `downloads_location` field in the `Config` is not set, it gets the local data directory path by calling the `get_local_data_dir` function and appends "bob" to it. +/// It then checks if the "bob" directory exists. If the directory does not exist, it attempts to create it. If the creation fails, it returns an error. +/// +/// # Arguments +/// +/// * `config` - A reference to a `Config` containing the application configuration. +/// +/// # Returns +/// +/// This function returns a `Result` that contains a `PathBuf` representing the downloads directory path if the operation was successful. +/// If the operation failed, the function returns `Err` with a description of the error. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// let downloads_directory = get_downloads_directory(&config).await?; +/// ``` pub async fn get_downloads_directory(config: &Config) -> Result { let path = match &config.downloads_location { Some(path) => { @@ -100,6 +174,27 @@ pub async fn get_downloads_directory(config: &Config) -> Result { Ok(path) } +/// Asynchronously returns the installation directory path based on the application configuration. +/// +/// This function takes a reference to a `Config` as an argument, which contains the application configuration. +/// It first checks if the `installation_location` field in the `Config` is set. If it is, it returns the value of this field as a `PathBuf`. +/// If the `installation_location` field in the `Config` is not set, it gets the downloads directory path by calling the `get_downloads_directory` function and appends "nvim-bin" to it. +/// +/// # Arguments +/// +/// * `config` - A reference to a `Config` containing the application configuration. +/// +/// # Returns +/// +/// This function returns a `Result` that contains a `PathBuf` representing the installation directory path if the operation was successful. +/// If the operation failed, the function returns `Err` with a description of the error. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// let installation_directory = get_installation_directory(&config).await?; +/// ``` pub async fn get_installation_directory(config: &Config) -> Result { match &config.installation_location { Some(path) => Ok(PathBuf::from(path.clone())), diff --git a/src/helpers/filesystem.rs b/src/helpers/filesystem.rs index b1912ec..6abe45e 100644 --- a/src/helpers/filesystem.rs +++ b/src/helpers/filesystem.rs @@ -4,6 +4,30 @@ use indicatif::{ProgressBar, ProgressStyle}; use std::path::Path; use tokio::fs; +/// Asynchronously removes a directory and all its contents. +/// +/// This function takes a string reference as an argument, which represents the directory to be removed. +/// It first reads the directory and counts the number of entries. Then, it creates a progress bar with the total number of entries. +/// It iterates over each entry in the directory. If the entry is a directory, it removes the directory and all its contents. If the entry is a file, it removes the file. +/// After removing each entry, it updates the progress bar. +/// Finally, it attempts to remove the directory itself. If this operation fails, it returns an error. +/// +/// # Arguments +/// +/// * `directory` - A string reference representing the directory to be removed. +/// +/// # Returns +/// +/// This function returns a `Result` that indicates whether the operation was successful. +/// If the operation was successful, the function returns `Ok(())`. +/// If the operation failed, the function returns `Err` with a description of the error. +/// +/// # Example +/// +/// ```rust +/// let directory = "/path/to/directory"; +/// remove_dir(directory).await; +/// ``` pub async fn remove_dir(directory: &str) -> Result<()> { let path = Path::new(directory); let size = path.read_dir()?.count(); @@ -38,6 +62,32 @@ pub async fn remove_dir(directory: &str) -> Result<()> { Ok(()) } +/// Asynchronously copies a directory from one location to another. +/// +/// This function takes two arguments: the source directory and the destination directory. Both arguments are implemented as references to `Path` and are static. +/// It first creates the destination directory, then reads the entries of the source directory. +/// For each entry in the source directory, it checks if the entry is a directory or a file. +/// If the entry is a directory, it recursively calls `copy_dir` to copy the directory to the destination. +/// If the entry is a file, it copies the file to the destination. +/// +/// # Arguments +/// +/// * `from` - A reference to a `Path` representing the source directory. +/// * `to` - A reference to a `Path` representing the destination directory. +/// +/// # Returns +/// +/// This function returns a `Result` that indicates whether the operation was successful. +/// If the operation was successful, the function returns `Ok(())`. +/// If the operation failed, the function returns `Err` with a description of the error. +/// +/// # Example +/// +/// ```rust +/// let from = Path::new("/path/to/source"); +/// let to = Path::new("/path/to/destination"); +/// copy_dir(from, to).await; +/// ``` #[async_recursion(?Send)] pub async fn copy_dir( from: impl AsRef + 'static, diff --git a/src/helpers/mod.rs b/src/helpers/mod.rs index c25fece..fd54d52 100644 --- a/src/helpers/mod.rs +++ b/src/helpers/mod.rs @@ -6,6 +6,22 @@ pub mod unarchive; pub mod version; use semver::Version; +/// Returns the file type for the Neovim binary download based on the target operating system. +/// +/// This function checks the target operating system using the `cfg!` macro and returns a string that corresponds to the appropriate file type for the Neovim binary download. +/// For Windows, it returns "zip". +/// For macOS, it returns "tar.gz". +/// For other operating systems, it returns "appimage". +/// +/// # Returns +/// +/// This function returns a `&'static str` that corresponds to the file type for the Neovim binary download. +/// +/// # Example +/// +/// ```rust +/// let file_type = get_file_type(); +/// ``` pub fn get_file_type() -> &'static str { if cfg!(target_family = "windows") { "zip" @@ -16,6 +32,28 @@ pub fn get_file_type() -> &'static str { } } +/// Returns the platform-specific name for the Neovim binary. +/// +/// This function takes an `Option` as an argument, which represents the version of Neovim. +/// It checks the target operating system and architecture using the `cfg!` macro and returns a string that corresponds to the appropriate Neovim binary for the platform. +/// For Windows, it returns "nvim-win64". +/// For macOS, it checks the version of Neovim. If the version is less than or equal to 0.9.5, it returns "nvim-macos". If the target architecture is "aarch64", it returns "nvim-macos-arm64". Otherwise, it returns "nvim-macos-x86_64". +/// For other operating systems, it returns "nvim-linux64". +/// +/// # Arguments +/// +/// * `version` - An `Option` representing the version of Neovim. +/// +/// # Returns +/// +/// This function returns a `&'static str` that corresponds to the platform-specific name for the Neovim binary. +/// +/// # Example +/// +/// ```rust +/// let version = Some(Version::new(0, 9, 5)); +/// let platform_name = get_platform_name(&version); +/// ``` pub fn get_platform_name(version: &Option) -> &'static str { if cfg!(target_os = "windows") { "nvim-win64" @@ -35,6 +73,28 @@ pub fn get_platform_name(version: &Option) -> &'static str { } } +/// Returns the platform-specific name for the Neovim download. +/// +/// This function takes an `Option` as an argument, which represents the version of Neovim to be downloaded. +/// It checks the target operating system and architecture using the `cfg!` macro and returns a string that corresponds to the appropriate Neovim download for the platform. +/// For Windows, it returns "nvim-win64". +/// For macOS, it checks the version of Neovim. If the version is less than or equal to 0.9.5, it returns "nvim-macos". If the target architecture is "aarch64", it returns "nvim-macos-arm64". Otherwise, it returns "nvim-macos-x86_64". +/// For other operating systems, it returns "nvim". +/// +/// # Arguments +/// +/// * `version` - An `Option` representing the version of Neovim to be downloaded. +/// +/// # Returns +/// +/// This function returns a `&'static str` that corresponds to the platform-specific name for the Neovim download. +/// +/// # Example +/// +/// ```rust +/// let version = Some(Version::new(0, 9, 5)); +/// let platform_name = get_platform_name_download(&version); +/// ``` pub fn get_platform_name_download(version: &Option) -> &'static str { if cfg!(target_os = "windows") { "nvim-win64" diff --git a/src/helpers/processes.rs b/src/helpers/processes.rs index 7551fa3..5de2578 100644 --- a/src/helpers/processes.rs +++ b/src/helpers/processes.rs @@ -5,6 +5,37 @@ use tokio::process::Command; use super::{directories, get_platform_name, version}; +/// Handles the execution of a subprocess. +/// +/// This function takes a mutable reference to a `Command` struct, which represents the subprocess to be executed. +/// It then awaits the status of the subprocess. +/// If the subprocess exits with a status code of `0`, the function returns `Ok(())`. +/// If the subprocess exits with a non-zero status code, the function returns an error with the status code as the error message. +/// If the subprocess is terminated by a signal, the function returns an error with the message "process terminated by signal". +/// +/// # Arguments +/// +/// * `process` - A mutable reference to a `Command` struct representing the subprocess to be executed. +/// +/// # Returns +/// +/// This function returns a `Result` that indicates whether the operation was successful. +/// If the operation was successful, the function returns `Ok(())`. +/// If the operation failed, the function returns `Err` with a description of the error. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The subprocess exits with a non-zero status code. +/// * The subprocess is terminated by a signal. +/// +/// # Example +/// +/// ```rust +/// let mut process = Command::new("ls"); +/// handle_subprocess(&mut process).await; +/// ``` pub async fn handle_subprocess(process: &mut Command) -> Result<()> { match process.status().await?.code() { Some(0) => Ok(()), @@ -13,6 +44,43 @@ pub async fn handle_subprocess(process: &mut Command) -> Result<()> { } } +/// Handles the execution of the Neovim process. +/// +/// This function takes a reference to a `Config` struct and a slice of `String` arguments. +/// It retrieves the downloads directory and the currently used version of Neovim from the configuration. +/// It then constructs the path to the Neovim binary and spawns a new process with the given arguments. +/// The function then enters a loop where it continuously checks the status of the spawned process. +/// If the process exits with a status code of `0`, the function returns `Ok(())`. +/// If the process exits with a non-zero status code, the function returns an error with the status code as the error message. +/// If the process is terminated by a signal, the function returns an error with the message "Process terminated by signal". +/// If the function fails to wait on the child process, it returns an error with the message "Failed to wait on child process". +/// +/// # Arguments +/// +/// * `config` - A reference to a `Config` struct containing the configuration for the Neovim process. +/// * `args` - A slice of `String` arguments to be passed to the Neovim process. +/// +/// # Returns +/// +/// This function returns a `Result` that indicates whether the operation was successful. +/// If the operation was successful, the function returns `Ok(())`. +/// If the operation failed, the function returns `Err` with a description of the error. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The Neovim process exits with a non-zero status code. +/// * The Neovim process is terminated by a signal. +/// * The function fails to wait on the child process. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// let args = vec!["-v".to_string()]; +/// handle_nvim_process(&config, &args).await; +/// ``` pub async fn handle_nvim_process(config: &Config, args: &[String]) -> Result<()> { let downloads_dir = directories::get_downloads_directory(config).await?; let used_version = version::get_current_version(config).await?; diff --git a/src/helpers/sync.rs b/src/helpers/sync.rs index 3174330..78d1e74 100644 --- a/src/helpers/sync.rs +++ b/src/helpers/sync.rs @@ -3,6 +3,37 @@ use anyhow::{anyhow, Result}; #[cfg(target_os = "linux")] use std::process::{Command, Stdio}; +/// Handles the execution of a subprocess. +/// +/// This function takes a mutable reference to a `Command` struct, which represents the subprocess to be executed. +/// It sets the standard output of the subprocess to `null` and then executes the subprocess. +/// If the subprocess exits with a status code of `0`, the function returns `Ok(())`. +/// If the subprocess exits with a non-zero status code, the function returns an error with the status code as the error message. +/// If the subprocess is terminated by a signal, the function returns an error with the message "process terminated by signal". +/// +/// # Arguments +/// +/// * `process` - A mutable reference to a `Command` struct representing the subprocess to be executed. +/// +/// # Returns +/// +/// This function returns a `Result` that indicates whether the operation was successful. +/// If the operation was successful, the function returns `Ok(())`. +/// If the operation failed, the function returns `Err` with a description of the error. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The subprocess exits with a non-zero status code. +/// * The subprocess is terminated by a signal. +/// +/// # Example +/// +/// ```rust +/// let mut process = Command::new("ls"); +/// handle_subprocess(&mut process); +/// ``` #[cfg(target_os = "linux")] pub fn handle_subprocess(process: &mut Command) -> Result<()> { match process.stdout(Stdio::null()).status()?.code() { diff --git a/src/helpers/unarchive.rs b/src/helpers/unarchive.rs index 71448dc..ff4e0a7 100644 --- a/src/helpers/unarchive.rs +++ b/src/helpers/unarchive.rs @@ -3,6 +3,42 @@ use std::fs; use super::version::types::LocalVersion; +/// Starts the process of expanding a downloaded file. +/// +/// This function is asynchronous and uses `tokio::task::spawn_blocking` to run the `expand` function in a separate thread. +/// It takes a `LocalVersion` struct which contains information about the downloaded file, such as its name, format, and path. +/// The function first clones the `LocalVersion` struct and passes it to the `expand` function. +/// If the `expand` function returns an error, the `start` function also returns an error. +/// If the `expand` function is successful, the `start` function removes the original downloaded file. +/// +/// # Arguments +/// +/// * `file` - A `LocalVersion` struct representing the downloaded file. +/// +/// # Returns +/// +/// This function returns a `Result` that indicates whether the operation was successful. +/// If the operation was successful, the function returns `Ok(())`. +/// If the operation failed, the function returns `Err` with a description of the error. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The `expand` function returns an error. +/// * The original downloaded file could not be removed. +/// +/// # Example +/// +/// ```rust +/// let downloaded_file = LocalVersion { +/// file_name: "nvim-linux", +/// file_format: "AppImage", +/// semver: semver::Version::parse("0.5.0").unwrap(), +/// path: "/path/to/downloaded/file", +/// }; +/// start(downloaded_file).await; +/// ``` pub async fn start(file: LocalVersion) -> Result<()> { let temp_file = file.clone(); match tokio::task::spawn_blocking(move || match expand(temp_file) { @@ -22,6 +58,49 @@ pub async fn start(file: LocalVersion) -> Result<()> { Ok(()) } +/// Expands a downloaded file on Linux. +/// +/// This function is specific to Linux due to the use of certain features like `os::unix::fs::PermissionsExt`. +/// It takes a `LocalVersion` struct which contains information about the downloaded file, such as its name and format. +/// The function then checks if a directory with the same name as the downloaded file exists, and if so, removes it. +/// It then sets the permissions of the downloaded file to `0o551` and extracts its contents using the `--appimage-extract` command. +/// After extraction, the function renames the `squashfs-root` directory to the name of the downloaded file and changes the current directory to the renamed directory. +/// It then removes certain files and renames the `usr` directory to `nvim-linux64`. +/// Finally, it changes the current directory back to the parent directory. +/// +/// # Arguments +/// +/// * `downloaded_file` - A `LocalVersion` struct representing the downloaded file. +/// +/// # Returns +/// +/// This function returns a `Result` that indicates whether the operation was successful. +/// If the operation was successful, the function returns `Ok(())`. +/// If the operation failed, the function returns `Err` with a description of the error. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * A directory with the same name as the downloaded file could not be removed. +/// * The permissions of the downloaded file could not be set. +/// * The downloaded file could not be extracted. +/// * The `squashfs-root` directory could not be renamed. +/// * The current directory could not be changed. +/// * Certain files could not be removed. +/// * The `usr` directory could not be renamed. +/// +/// # Example +/// +/// ```rust +/// let downloaded_file = LocalVersion { +/// file_name: "nvim-linux", +/// file_format: "AppImage", +/// semver: semver::Version::parse("0.5.0").unwrap(), +/// path: "/path/to/downloaded/file", +/// }; +/// expand(downloaded_file); +/// ``` #[cfg(target_os = "linux")] fn expand(downloaded_file: LocalVersion) -> Result<()> { use super::sync; @@ -60,6 +139,43 @@ fn expand(downloaded_file: LocalVersion) -> Result<()> { Ok(()) } +/// Expands a downloaded file on Windows. +/// +/// This function is specific to Windows due to the use of certain features like `zip::ZipArchive`. +/// It takes a `LocalVersion` struct which contains information about the downloaded file, such as its name and format. +/// The function then opens the file and extracts its contents using `zip::ZipArchive`. +/// During the extraction process, a progress bar is displayed to the user. +/// After extraction, the function removes the original zip file. +/// +/// # Arguments +/// +/// * `downloaded_file` - A `LocalVersion` struct representing the downloaded file. +/// +/// # Returns +/// +/// This function returns a `Result` that indicates whether the operation was successful. +/// If the operation was successful, the function returns `Ok(())`. +/// If the operation failed, the function returns `Err` with a description of the error. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The downloaded file could not be opened. +/// * The file could not be extracted. +/// * The original zip file could not be removed. +/// +/// # Example +/// +/// ```rust +/// let downloaded_file = LocalVersion { +/// file_name: "nvim-windows", +/// file_format: "zip", +/// semver: semver::Version::parse("0.5.0").unwrap(), +/// path: "/path/to/downloaded/file", +/// }; +/// expand(downloaded_file); +/// ``` #[cfg(target_family = "windows")] fn expand(downloaded_file: LocalVersion) -> Result<()> { use indicatif::{ProgressBar, ProgressStyle}; @@ -122,6 +238,45 @@ fn expand(downloaded_file: LocalVersion) -> Result<()> { Ok(()) } +/// Expands a downloaded file on macOS. +/// +/// This function is specific to macOS due to the use of certain features like `os::unix::fs::PermissionsExt`. +/// It takes a `LocalVersion` struct which contains information about the downloaded file, such as its name and format. +/// The function then opens the file, decompresses it using `GzDecoder`, and extracts its contents using `tar::Archive`. +/// During the extraction process, a progress bar is displayed to the user. +/// After extraction, the function renames the `nvim-osx64` directory to `nvim-macos` if it exists. +/// Finally, it sets the permissions of the `nvim` binary to `0o551`. +/// +/// # Arguments +/// +/// * `downloaded_file` - A `LocalVersion` struct representing the downloaded file. +/// +/// # Returns +/// +/// This function returns a `Result` that indicates whether the operation was successful. +/// If the operation was successful, the function returns `Ok(())`. +/// If the operation failed, the function returns `Err` with a description of the error. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The downloaded file could not be opened. +/// * The file could not be decompressed or extracted. +/// * The `nvim-osx64` directory could not be renamed. +/// * The permissions of the `nvim` binary could not be set. +/// +/// # Example +/// +/// ```rust +/// let downloaded_file = LocalVersion { +/// file_name: "nvim-macos", +/// file_format: "tar.gz", +/// semver: semver::Version::parse("0.5.0").unwrap(), +/// path: "/path/to/downloaded/file", +/// }; +/// expand(downloaded_file); +/// ``` #[cfg(target_os = "macos")] // I don't know if its worth making both expand functions into one function, but the API difference will cause so much if statements fn expand(downloaded_file: LocalVersion) -> Result<()> { use crate::helpers; diff --git a/src/helpers/version/mod.rs b/src/helpers/version/mod.rs index d4a5a7c..a511922 100644 --- a/src/helpers/version/mod.rs +++ b/src/helpers/version/mod.rs @@ -18,6 +18,34 @@ use tokio::{ }; use tracing::info; +/// Parses the version type from a version string. +/// +/// This function takes a version string and determines the type of the version. It supports the following version types: `Nightly`, `Latest`, `Hash`, `Normal`, and `NightlyRollback`. +/// +/// # Arguments +/// +/// * `client` - The client to use for fetching the latest version or commit. +/// * `version` - The version string to parse. +/// +/// # Returns +/// +/// * `Result` - Returns a `Result` that contains a `ParsedVersion` struct with the parsed version information, or an error if the operation failed or the version string is not valid. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The version string is not valid. +/// * The latest version or commit cannot be fetched. +/// +/// # Example +/// +/// ```rust +/// let client = Client::new(); +/// let version = "nightly"; +/// let parsed_version = parse_version_type(&client, version).await.unwrap(); +/// println!("The parsed version is {:?}", parsed_version); +/// ``` pub async fn parse_version_type(client: &Client, version: &str) -> Result { match version { "nightly" => Ok(ParsedVersion { @@ -87,6 +115,31 @@ pub async fn parse_version_type(client: &Client, version: &str) -> Result>` - Returns a `Result` that contains an `Option` with the `PathBuf` to the version sync file, or `None` if the `version_sync_file_location` field is `None`, or an error if the operation failed. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The file at the specified path cannot be created. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// let version_sync_file_location = get_version_sync_file_location(&config).await.unwrap(); +/// println!("The version sync file is located at {:?}", version_sync_file_location); +/// ``` pub async fn get_version_sync_file_location(config: &Config) -> Result> { let path = match &config.version_sync_file_location { Some(path) => { @@ -103,6 +156,34 @@ pub async fn get_version_sync_file_location(config: &Config) -> Result` - Returns a `Result` that contains `true` if the version is installed, `false` otherwise, or an error if the operation failed. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The downloads directory cannot be retrieved. +/// * The downloads directory cannot be read. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// let version = "1.0.0"; +/// let is_installed = is_version_installed(version, &config).await.unwrap(); +/// println!("Is version {} installed? {}", version, is_installed); +/// ``` pub async fn is_version_installed(version: &str, config: &Config) -> Result { let downloads_dir = directories::get_downloads_directory(config).await?; let mut dir = tokio::fs::read_dir(&downloads_dir).await?; @@ -118,6 +199,31 @@ pub async fn is_version_installed(version: &str, config: &Config) -> Result` - Returns a `Result` that contains the current version of Neovim being used, or an error if the operation failed. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The downloads directory cannot be retrieved. +/// * The "used" file cannot be read. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// let current_version = get_current_version(&config).await.unwrap(); +/// println!("The current version is {}", current_version); pub async fn get_current_version(config: &Config) -> Result { let mut downloads_dir = directories::get_downloads_directory(config).await?; downloads_dir.push("used"); @@ -125,6 +231,27 @@ pub async fn get_current_version(config: &Config) -> Result { .map_err(|_| anyhow!("The used file required for bob could not be found. This could mean that Neovim is not installed through bob.")) } +/// Checks if a specific version is currently being used. +/// +/// This function retrieves the current version from the configuration and checks if it matches the specified version. +/// +/// # Arguments +/// +/// * `version` - The version to check. +/// * `config` - The configuration to retrieve the current version from. +/// +/// # Returns +/// +/// * `bool` - Returns `true` if the specified version is currently being used, `false` otherwise. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// let version = "1.0.0"; +/// let is_used = is_version_used(version, &config).await; +/// println!("Is version {} used? {}", version, is_used); +/// ``` pub async fn is_version_used(version: &str, config: &Config) -> bool { match get_current_version(config).await { Ok(value) => value.eq(version), @@ -132,6 +259,33 @@ pub async fn is_version_used(version: &str, config: &Config) -> bool { } } +/// Searches for the stable version of Neovim from the GitHub releases. +/// +/// This function sends a GET request to the GitHub API to fetch the latest 10 releases of the Neovim repository. It then deserializes the response into a vector of `UpstreamVersion` objects and finds the stable release and the version of the stable release. +/// +/// # Arguments +/// +/// * `client` - The HTTP client to use for the request. +/// +/// # Returns +/// +/// * `Result` - Returns a `Result` that contains the tag name of the stable version, or an error if the operation failed. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The GET request to the GitHub API fails. +/// * The response from the GitHub API cannot be deserialized into a vector of `UpstreamVersion` objects. +/// * The stable release or the version of the stable release cannot be found. +/// +/// # Example +/// +/// ```rust +/// let client = Client::new(); +/// let stable_version = search_stable_version(&client).await.unwrap(); +/// println!("The stable version is {}", stable_version); +/// `` async fn search_stable_version(client: &Client) -> Result { let response = client .get("https://api.github.com/repos/neovim/neovim/releases?per_page=10") @@ -154,6 +308,32 @@ async fn search_stable_version(client: &Client) -> Result { Ok(stable_pin_release.tag_name.clone()) } +/// Fetches the latest commit from the Neovim repository on GitHub. +/// +/// This function sends a GET request to the GitHub API to fetch the latest commit from the master branch of the Neovim repository. It then deserializes the response into a `RepoCommit` object and returns the SHA of the commit. +/// +/// # Arguments +/// +/// * `client` - The HTTP client to use for the request. +/// +/// # Returns +/// +/// * `Result` - Returns a `Result` that contains the SHA of the latest commit, or an error if the operation failed. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The GET request to the GitHub API fails. +/// * The response from the GitHub API cannot be deserialized into a `RepoCommit` object. +/// +/// # Example +/// +/// ```rust +/// let client = Client::new(); +/// let latest_commit = get_latest_commit(&client).await.unwrap(); +/// println!("The latest commit is {}", latest_commit); +/// ``` async fn get_latest_commit(client: &Client) -> Result { let response = client .get("https://api.github.com/repos/neovim/neovim/commits/master") diff --git a/src/helpers/version/nightly.rs b/src/helpers/version/nightly.rs index c482aa4..b4ae966 100644 --- a/src/helpers/version/nightly.rs +++ b/src/helpers/version/nightly.rs @@ -5,6 +5,33 @@ use tokio::fs; use super::types::LocalNightly; use crate::{config::Config, github_requests::UpstreamVersion, helpers::directories}; +/// Retrieves the local nightly version. +/// +/// This function reads the `bob.json` file in the `nightly` directory of the downloads directory and parses it into an `UpstreamVersion` struct. +/// +/// # Arguments +/// +/// * `config` - The configuration to retrieve the downloads directory from. +/// +/// # Returns +/// +/// * `Result` - Returns a `Result` that contains an `UpstreamVersion` struct with the local nightly version, or an error if the operation failed. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The downloads directory cannot be retrieved. +/// * The `bob.json` file cannot be read. +/// * The `bob.json` file cannot be parsed into an `UpstreamVersion` struct. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// let local_nightly = get_local_nightly(&config).await.unwrap(); +/// println!("The local nightly version is {:?}", local_nightly); +/// ``` pub async fn get_local_nightly(config: &Config) -> Result { let downloads_dir = directories::get_downloads_directory(config).await?; if let Ok(file) = @@ -17,6 +44,35 @@ pub async fn get_local_nightly(config: &Config) -> Result { } } +/// Produces a vector of `LocalNightly` structs from the downloads directory. +/// +/// This function reads the downloads directory and creates a `LocalNightly` struct for each directory that matches the `nightly-[a-zA-Z0-9]{7,8}` pattern. The `LocalNightly` structs are sorted by the `published_at` field in descending order. +/// +/// # Arguments +/// +/// * `config` - The configuration to retrieve the downloads directory from. +/// +/// # Returns +/// +/// * `Result>` - Returns a `Result` that contains a vector of `LocalNightly` structs, or an error if the operation failed. +/// +/// # Errors +/// +/// This function will return an error if: +/// +/// * The downloads directory cannot be retrieved. +/// * The downloads directory cannot be read. +/// * A directory name does not match the `nightly-[a-zA-Z0-9]{7,8}` pattern. +/// * The `bob.json` file in a directory cannot be read. +/// * The `bob.json` file in a directory cannot be parsed into a `UpstreamVersion` struct. +/// +/// # Example +/// +/// ```rust +/// let config = Config::default(); +/// let nightly_vec = produce_nightly_vec(&config).await.unwrap(); +/// println!("There are {} nightly versions.", nightly_vec.len()); +/// ``` pub async fn produce_nightly_vec(config: &Config) -> Result> { let downloads_dir = directories::get_downloads_directory(config).await?; let mut paths = fs::read_dir(&downloads_dir).await?; diff --git a/src/helpers/version/types.rs b/src/helpers/version/types.rs index e639236..0dbdf9a 100644 --- a/src/helpers/version/types.rs +++ b/src/helpers/version/types.rs @@ -3,6 +3,28 @@ use semver::Version; use crate::github_requests::UpstreamVersion; use std::path::PathBuf; +/// Represents a parsed version of the software. +/// +/// This struct contains information about a parsed version of the software, including the tag name, version type, non-parsed string, and semantic version. +/// +/// # Fields +/// +/// * `tag_name: String` - The tag name of the parsed version. +/// * `version_type: VersionType` - The type of the parsed version. +/// * `non_parsed_string: String` - The non-parsed string of the parsed version. +/// * `semver: Option` - The semantic version of the parsed version, or `None` if the version is not a semantic version. +/// +/// # Example +/// +/// ```rust +/// let parsed_version = ParsedVersion { +/// tag_name: "v1.0.0".to_string(), +/// version_type: VersionType::Normal, +/// non_parsed_string: "version-1.0.0".to_string(), +/// semver: Some(Version::parse("1.0.0").unwrap()), +/// }; +/// println!("The parsed version is {:?}", parsed_version); +/// ``` pub struct ParsedVersion { pub tag_name: String, pub version_type: VersionType, @@ -10,6 +32,30 @@ pub struct ParsedVersion { pub semver: Option, } +/// Represents the type of a software version. +/// +/// This enum is used to distinguish between different types of software versions, such as normal versions, the latest version, nightly versions, versions identified by a hash, and nightly versions that have been rolled back. +/// +/// # Variants +/// +/// * `Normal` - Represents a normal version. +/// * `Latest` - Represents the latest version. +/// * `Nightly` - Represents a nightly version. +/// * `Hash` - Represents a version identified by a hash. +/// * `NightlyRollback` - Represents a nightly version that has been rolled back. +/// +/// # Example +/// +/// ```rust +/// let version_type = VersionType::Nightly; +/// match version_type { +/// VersionType::Normal => println!("This is a normal version."), +/// VersionType::Latest => println!("This is the latest version."), +/// VersionType::Nightly => println!("This is a nightly version."), +/// VersionType::Hash => println!("This is a version identified by a hash."), +/// VersionType::NightlyRollback => println!("This is a nightly version that has been rolled back."), +/// } +/// ``` #[derive(PartialEq, Eq, Debug)] pub enum VersionType { Normal, @@ -19,12 +65,55 @@ pub enum VersionType { NightlyRollback, } +/// Represents a local nightly version of the software. +/// +/// This struct contains information about a local nightly version of the software, including the upstream version data and the path to the version file. +/// +/// # Fields +/// +/// * `data: UpstreamVersion` - The upstream version data for the local nightly version. +/// * `path: PathBuf` - The path to the file that contains the local nightly version. +/// +/// # Example +/// +/// ```rust +/// let upstream_version = UpstreamVersion { +/// // initialize fields +/// }; +/// let local_nightly = LocalNightly { +/// data: upstream_version, +/// path: PathBuf::from("/path/to/nightly/version"), +/// }; +/// println!("The local nightly version is {:?}", local_nightly); +/// ``` #[derive(Debug, Clone)] pub struct LocalNightly { pub data: UpstreamVersion, pub path: PathBuf, } +/// Represents a local version of the software. +/// +/// This struct contains information about a local version of the software, including the file name, file format, path, and semantic version. +/// +/// # Fields +/// +/// * `file_name: String` - The name of the file that contains the local version. +/// * `file_format: String` - The format of the file that contains the local version. +/// * `path: String` - The path to the file that contains the local version. +/// * `semver: Option` - The semantic version of the local version, or `None` if the version is not a semantic version. +/// +/// # Example +/// +/// ```rust +/// let local_version = LocalVersion { +/// file_name: "version-1.0.0.tar.gz".to_string(), +/// file_format: "tar.gz".to_string(), +/// path: "/path/to/version-1.0.0.tar.gz".to_string(), +/// semver: Some(Version::parse("1.0.0").unwrap()), +/// }; +/// println!("The local version is {:?}", local_version); +/// ``` #[derive(Clone, PartialEq)] pub struct LocalVersion { pub file_name: String,