From 7b58e9e9a698bb068ec2f72a713ddd8df9cf19b6 Mon Sep 17 00:00:00 2001 From: TheAwiteb Date: Tue, 26 Dec 2023 20:53:07 +0300 Subject: [PATCH] Rename the project to `lprs` --- CONTRIBUTING.md | 20 +++++++++--------- Cargo.lock | 44 +++++++++++++++++++-------------------- Cargo.toml | 4 ++-- README.md | 32 ++++++++++++++-------------- src/cli/add_command.rs | 4 ++-- src/cli/clean_command.rs | 6 +++--- src/cli/edit_command.rs | 8 +++---- src/cli/gen_command.rs | 6 +++--- src/cli/list_command.rs | 10 ++++----- src/cli/mod.rs | 10 ++++----- src/cli/remove_command.rs | 6 +++--- src/macros.rs | 8 +++---- src/main.rs | 2 +- src/password/cipher.rs | 14 ++++++------- src/password/mod.rs | 18 ++++++++-------- src/password/validator.rs | 4 ++-- src/traits.rs | 4 ++-- src/utils.rs | 8 +++---- 18 files changed, 104 insertions(+), 104 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1c94ee7..32cff0c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,23 +1,23 @@ -# Contributing to passrs +# Contributing to lprs -Thank you for your interest in contributing to passrs! We welcome contributions from the community to help improve the project. +Thank you for your interest in contributing to lprs! We welcome contributions from the community to help improve the project. ## Getting Started -To get started with contributing to passrs, please follow these steps: +To get started with contributing to lprs, please follow these steps: -1. Fork the passrs repository to your own GitHub account. +1. Fork the lprs repository to your own GitHub account. 2. Clone the forked repository to your local machine. 3. Create a new branch for your changes: `git checkout -b my-branch-name`. 4. Make your desired changes to the codebase. 5. Test your changes to ensure they work as expected. 6. Commit your changes: `git commit -m "Add my changes"`. 7. Push your changes to your forked repository: `git push origin my-branch-name`. -8. Open a pull request from your forked repository to the main passrs repository. +8. Open a pull request from your forked repository to the main lprs repository. ## Code Style -Please follow the existing code style and conventions used in the passrs project. This includes: +Please follow the existing code style and conventions used in the lprs project. This includes: - Using Rust's official formatting tool, `rustfmt`, to format your code. - Writing clear and concise code with meaningful variable and function names. @@ -25,18 +25,18 @@ Please follow the existing code style and conventions used in the passrs project ## Reporting Issues -If you encounter any issues or bugs while using passrs, please open a new issue on the GitHub repository. When reporting an issue, please provide as much detail as possible, including steps to reproduce the issue and any relevant error messages. +If you encounter any issues or bugs while using lprs, please open a new issue on the GitHub repository. When reporting an issue, please provide as much detail as possible, including steps to reproduce the issue and any relevant error messages. ## Feature Requests -If you have a feature request or an idea for improving passrs, we encourage you to open a new issue on the GitHub repository. Please describe the feature or improvement in detail and provide any relevant context or examples. +If you have a feature request or an idea for improving lprs, we encourage you to open a new issue on the GitHub repository. Please describe the feature or improvement in detail and provide any relevant context or examples. ## Code Review -All contributions to passrs will go through a code review process. This ensures that the code meets the project's standards and maintains its quality. Please be open to feedback and suggestions from the project maintainers during the code review process. +All contributions to lprs will go through a code review process. This ensures that the code meets the project's standards and maintains its quality. Please be open to feedback and suggestions from the project maintainers during the code review process. ## License -By contributing to passrs, you agree that your contributions will be licensed under the project's [LICENSE](LICENSE) file. This means that you are granting passrs the right to use, modify, and distribute your contributions under the terms of the license. wich is GPL-3.0 License. +By contributing to lprs, you agree that your contributions will be licensed under the project's [LICENSE](LICENSE) file. This means that you are granting lprs the right to use, modify, and distribute your contributions under the terms of the license. wich is GPL-3.0 License. Happy contributing! diff --git a/Cargo.lock b/Cargo.lock index 690ba48..9b1b1fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -451,6 +451,28 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "lprs" +version = "0.1.0" +dependencies = [ + "base64", + "clap", + "comfy-table", + "directories", + "log", + "passwords", + "pretty_env_logger", + "regex", + "scanpw", + "serde", + "serde_json", + "serde_with_macros", + "sha256", + "soft-aes", + "thiserror", + "url", +] + [[package]] name = "memchr" version = "2.6.4" @@ -520,28 +542,6 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "passrs" -version = "0.1.0" -dependencies = [ - "base64", - "clap", - "comfy-table", - "directories", - "log", - "passwords", - "pretty_env_logger", - "regex", - "scanpw", - "serde", - "serde_json", - "serde_with_macros", - "sha256", - "soft-aes", - "thiserror", - "url", -] - [[package]] name = "passwords" version = "3.1.16" diff --git a/Cargo.toml b/Cargo.toml index f0a870c..b539277 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "passrs" +name = "lprs" version = "0.1.0" edition = "2021" license = "GPL-3.0-only" authors = ["Awiteb "] readme = "README.md" description = "Local CLI password manager" -repository = "https://github.com/TheAwiteb/passrs" +repository = "https://github.com/TheAwiteb/lprs" rust-version = "1.70.0" keywords = ["password", "manager", "CLI"] categories = ["command-line-utilities"] diff --git a/README.md b/README.md index 061cc41..fb81d5a 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,34 @@ -# Passrs +# Lprs -Passrs is a local password manager designed to securely store and manage your passwords. +Lprs is a local password manager designed to securely store and manage your passwords. ## Installation -To install Passrs, you will need to have the Cargo package manager installed. If you do not have Cargo installed, you can install it by following the instructions [here](https://doc.rust-lang.org/cargo/getting-started/installation.html). Note the Minimum Supported Rust Version (MSRV) for Passrs is `1.70.0`. +To install Lprs, you will need to have the Cargo package manager installed. If you do not have Cargo installed, you can install it by following the instructions [here](https://doc.rust-lang.org/cargo/getting-started/installation.html). Note the Minimum Supported Rust Version (MSRV) for Lprs is `1.70.0`. -1. Clone the Passrs repository: +1. Clone the Lprs repository: ```bash - cargo install --locked --git https://github.com/theawiteb/passrs.git + cargo install --locked --git https://github.com/theawiteb/lprs.git ``` -2. Run Passrs: +2. Run Lprs: ```bash - passrs --help + lprs --help ``` ## Uninstallation ```bash -cargo uninstall passrs +cargo uninstall lprs ``` ## Usage -Passrs provides a command-line interface for managing your passwords. The following commands are available: +Lprs provides a command-line interface for managing your passwords. The following commands are available: ``` Local CLI password manager -Usage: passrs [OPTIONS] +Usage: lprs [OPTIONS] Commands: add Add new password @@ -41,7 +41,7 @@ Commands: Options: -p, --passwords-file - The passwords json file, default: $HOME/.local/share/passrs/passwords.json + The passwords json file, default: $HOME/.local/share/lprs/passwords.json -h, --help Print help -V, --version @@ -50,13 +50,13 @@ Options: ### Example ```bash -passrs add -n "Gmail" -u "some@gmail.com" -p $(passrs gen 19 -u -l -s) -s "https://mail.google.com" +lprs add -n "Gmail" -u "some@gmail.com" -p $(lprs gen 19 -u -l -s) -s "https://mail.google.com" ``` #### Result This is the result when search for it ``` -$ passrs list -e "mail" -p -s +$ lprs list -e "mail" -p -s Master Password: *************** +-------+-------+----------------+---------------------+-------------------------+ | Index | Name | Username | Password | Service | @@ -69,13 +69,13 @@ Master Password: *************** +It is important to regularly backup your passwords to prevent data loss. Lprs does not provide an automatic backup feature. To backup your passwords, you can use the export command provided by Lprs. This command allows you to export your encrypted passwords to a json file, which you can then manually backup to a secure location. --> ## Contributing -Contributions to Passrs are welcome! If you would like to contribute, please follow the guidelines outlined in the [CONTRIBUTING](CONTRIBUTING.md) file. +Contributions to Lprs are welcome! If you would like to contribute, please follow the guidelines outlined in the [CONTRIBUTING](CONTRIBUTING.md) file. ## License -Passrs is licensed under the GPL-3.0 License. This means that you are free to use, modify, and distribute the software under the terms of this license. Please refer to the [LICENSE](LICENSE) file for more details. +Lprs is licensed under the GPL-3.0 License. This means that you are free to use, modify, and distribute the software under the terms of this license. Please refer to the [LICENSE](LICENSE) file for more details. diff --git a/src/cli/add_command.rs b/src/cli/add_command.rs index 37fe700..ff47213 100644 --- a/src/cli/add_command.rs +++ b/src/cli/add_command.rs @@ -18,7 +18,7 @@ use clap::Args; use crate::{ password::{Password, Passwords}, - PassrsResult, RunCommand, + LprsResult, RunCommand, }; #[derive(Debug, Args)] @@ -29,7 +29,7 @@ pub struct Add { } impl RunCommand for Add { - fn run(&self, mut password_manager: Passwords) -> PassrsResult<()> { + fn run(&self, mut password_manager: Passwords) -> LprsResult<()> { password_manager.add_password(self.password_info.clone()); password_manager.try_export() } diff --git a/src/cli/clean_command.rs b/src/cli/clean_command.rs index 0fc870b..2104359 100644 --- a/src/cli/clean_command.rs +++ b/src/cli/clean_command.rs @@ -18,14 +18,14 @@ use std::fs; use clap::Args; -use crate::{password::Passwords, PassrsError, PassrsResult, RunCommand}; +use crate::{password::Passwords, LprsError, LprsResult, RunCommand}; #[derive(Debug, Args)] #[command(author, version, about, long_about = None)] pub struct Clean {} impl RunCommand for Clean { - fn run(&self, password_manager: Passwords) -> PassrsResult<()> { - fs::write(password_manager.passwords_file, "[]").map_err(PassrsError::Io) + fn run(&self, password_manager: Passwords) -> LprsResult<()> { + fs::write(password_manager.passwords_file, "[]").map_err(LprsError::Io) } } diff --git a/src/cli/edit_command.rs b/src/cli/edit_command.rs index 83de8af..be3ca79 100644 --- a/src/cli/edit_command.rs +++ b/src/cli/edit_command.rs @@ -20,7 +20,7 @@ use clap::Args; use crate::{ password::{Password, Passwords}, - PassrsError, PassrsResult, RunCommand, + LprsError, LprsResult, RunCommand, }; #[derive(Debug, Args)] @@ -47,7 +47,7 @@ pub struct Edit { } impl RunCommand for Edit { - fn run(&self, mut password_manager: Passwords) -> PassrsResult<()> { + fn run(&self, mut password_manager: Passwords) -> LprsResult<()> { let index = self.index.get() as usize; if let Some(password) = password_manager.passwords.get_mut(index - 1) { @@ -57,7 +57,7 @@ impl RunCommand for Edit { && self.service.is_none() && self.note.is_none() { - Err(PassrsError::Other( + Err(LprsError::Other( "You must edit one option at least".to_owned(), )) } else { @@ -79,7 +79,7 @@ impl RunCommand for Edit { password_manager.try_export() } } else { - Err(PassrsError::InvalidPasswordIndex(format!( + Err(LprsError::InvalidPasswordIndex(format!( "The index `{}` is greater than the passwords count {}", self.index, password_manager.passwords.len() diff --git a/src/cli/gen_command.rs b/src/cli/gen_command.rs index 5517aae..52c0019 100644 --- a/src/cli/gen_command.rs +++ b/src/cli/gen_command.rs @@ -18,7 +18,7 @@ use std::num::NonZeroU64; use clap::Args; -use crate::{password::Passwords, PassrsError, PassrsResult, RunCommand}; +use crate::{password::Passwords, LprsError, LprsResult, RunCommand}; #[derive(Debug, Args)] #[command(author, version, about, long_about = None)] @@ -42,7 +42,7 @@ pub struct Gen { } impl RunCommand for Gen { - fn run(&self, _password_manager: Passwords) -> PassrsResult<()> { + fn run(&self, _password_manager: Passwords) -> LprsResult<()> { if self.uppercase || self.lowercase || self.numbers || self.symbols { println!( "{}", @@ -58,7 +58,7 @@ impl RunCommand for Gen { ); Ok(()) } else { - Err(PassrsError::Other( + Err(LprsError::Other( "You need to enable at least one kind of characters".to_owned(), )) } diff --git a/src/cli/list_command.rs b/src/cli/list_command.rs index 917770c..1f21708 100644 --- a/src/cli/list_command.rs +++ b/src/cli/list_command.rs @@ -20,7 +20,7 @@ use clap::Args; use comfy_table::Table; use regex::Regex; -use crate::{password::Passwords, PassrsError, PassrsResult, RunCommand}; +use crate::{password::Passwords, LprsError, LprsResult, RunCommand}; #[derive(Debug, Args)] #[command(author, version, about, long_about = None)] @@ -49,19 +49,19 @@ pub struct List { } impl RunCommand for List { - fn run(&self, password_manager: Passwords) -> PassrsResult<()> { + fn run(&self, password_manager: Passwords) -> LprsResult<()> { if password_manager.passwords.is_empty() { - Err(PassrsError::Other( + Err(LprsError::Other( "Looks like there is no passwords to list".to_owned(), )) } else { if self.get.is_some() && self.search.is_some() { - return Err(PassrsError::ArgsConflict( + return Err(LprsError::ArgsConflict( "You cannot use `--get` arg with `--search` arg".to_owned(), )); } if self.regex && self.search.is_none() { - return Err(PassrsError::ArgsConflict( + return Err(LprsError::ArgsConflict( "You cannot use `--regex` without `--search` arg".to_owned(), )); } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index f541683..d9aa12e 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -20,7 +20,7 @@ use clap::Parser; use crate::{ password::{self, Passwords}, - PassrsError, PassrsResult, RunCommand, + LprsError, LprsResult, RunCommand, }; pub mod add_command; @@ -45,7 +45,7 @@ crate::create_commands!( #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] pub struct Cli { - /// The passwords json file, default: $HOME/.local/share/passrs/passwords.json + /// The passwords json file, default: $HOME/.local/share/lprs/passwords.json #[arg(short, long)] passwords_file: Option, @@ -56,7 +56,7 @@ pub struct Cli { impl Cli { /// Run the cli - pub fn run(self) -> PassrsResult<()> { + pub fn run(self) -> LprsResult<()> { let passwords_file = if let Some(ref path) = self.passwords_file { path.clone() } else { @@ -77,11 +77,11 @@ impl Cli { if password::is_new_password_file(&passwords_file)? { let analyzed = passwords::analyzer::analyze(&password); if analyzed.length() < 15 { - return Err(PassrsError::WeakPassword( + return Err(LprsError::WeakPassword( "The password length must be beggier then 15".to_owned(), )); } else if passwords::scorer::score(&analyzed) < 80.0 { - return Err(PassrsError::WeakPassword( + return Err(LprsError::WeakPassword( "Your password is not stronge enough".to_owned(), )); } diff --git a/src/cli/remove_command.rs b/src/cli/remove_command.rs index 1c80414..79ffc10 100644 --- a/src/cli/remove_command.rs +++ b/src/cli/remove_command.rs @@ -18,7 +18,7 @@ use std::num::NonZeroU64; use clap::Args; -use crate::{password::Passwords, PassrsError, PassrsResult, RunCommand}; +use crate::{password::Passwords, LprsError, LprsResult, RunCommand}; #[derive(Debug, Args)] #[command(author, version, about, long_about = None)] @@ -32,11 +32,11 @@ pub struct Remove { } impl RunCommand for Remove { - fn run(&self, mut password_manager: Passwords) -> PassrsResult<()> { + fn run(&self, mut password_manager: Passwords) -> LprsResult<()> { let index = (self.index.get() - 1) as usize; if index > password_manager.passwords.len() { if !self.force { - return Err(PassrsError::Other( + return Err(LprsError::Other( "The index is greater than the passwords counts".to_owned(), )); } diff --git a/src/macros.rs b/src/macros.rs index 2327da9..bfa7955 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -29,7 +29,7 @@ /// ``` /// #### Output /// ```rust -/// ///The passrs commands +/// ///The lprs commands /// pub enum TestCommands { /// ///Test command /// Test(TestArgs), @@ -41,7 +41,7 @@ /// fn run( /// &self, /// password_manager: crate::password::Passwords, -/// ) -> crate::PassrsResult<()> { +/// ) -> crate::LprsResult<()> { /// match self { /// Self::Test(command) => command.run(password_manager), /// Self::Some(command) => command.run(password_manager), @@ -52,7 +52,7 @@ #[macro_export] macro_rules! create_commands { (enum $enum_name: ident $($doc:tt, $varint: ident => $command: ty)+) => { - #[doc = "The passrs commands"] + #[doc = "The lprs commands"] #[derive(Debug, clap::Subcommand)] pub enum $enum_name { $( @@ -63,7 +63,7 @@ macro_rules! create_commands { #[automatically_derived] impl $crate::RunCommand for $enum_name{ - fn run(&self, password_manager: $crate::password::Passwords) -> $crate::PassrsResult<()> { + fn run(&self, password_manager: $crate::password::Passwords) -> $crate::LprsResult<()> { match self { $( Self::$varint(command) => command.run(password_manager), diff --git a/src/main.rs b/src/main.rs index 7226903..c19d1db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,7 +32,7 @@ mod traits; pub use {macros::*, traits::*}; -pub use errors::{Error as PassrsError, Result as PassrsResult}; +pub use errors::{Error as LprsError, Result as LprsResult}; pub const STANDARDBASE: GeneralPurpose = GeneralPurpose::new(&alphabet::STANDARD, PAD); diff --git a/src/password/cipher.rs b/src/password/cipher.rs index 57bb7b9..ecd3749 100644 --- a/src/password/cipher.rs +++ b/src/password/cipher.rs @@ -17,19 +17,19 @@ use base64::Engine; use soft_aes::aes::{aes_dec_ecb, aes_enc_ecb}; -use crate::{PassrsError, PassrsResult}; +use crate::{LprsError, LprsResult}; /// Encrypt the string with AEC ECB -pub fn encrypt(master_password: &[u8], data: &str) -> PassrsResult { +pub fn encrypt(master_password: &[u8], data: &str) -> LprsResult { let padding = Some("PKCS7"); aes_enc_ecb(data.as_bytes(), master_password, padding) .map(|d| crate::STANDARDBASE.encode(d)) - .map_err(|err| PassrsError::Encryption(err.to_string())) + .map_err(|err| LprsError::Encryption(err.to_string())) } /// Decrypt the string with AEC ECB -pub fn decrypt(master_password: &[u8], data: &str) -> PassrsResult { +pub fn decrypt(master_password: &[u8], data: &str) -> LprsResult { let padding = Some("PKCS7"); aes_dec_ecb( @@ -39,10 +39,10 @@ pub fn decrypt(master_password: &[u8], data: &str) -> PassrsResult { ) .map_err(|err| { if err.to_string().contains("Invalid padding") { - PassrsError::WrongMasterPassword + LprsError::WrongMasterPassword } else { - PassrsError::Decryption(err.to_string()) + LprsError::Decryption(err.to_string()) } }) - .map(|d| String::from_utf8(d).map_err(PassrsError::Utf8))? + .map(|d| String::from_utf8(d).map_err(LprsError::Utf8))? } diff --git a/src/password/mod.rs b/src/password/mod.rs index 29ffc98..f50873b 100644 --- a/src/password/mod.rs +++ b/src/password/mod.rs @@ -19,7 +19,7 @@ use std::{fs, path::PathBuf}; use clap::Parser; use serde::{Deserialize, Serialize}; -use crate::{PassrsError, PassrsResult}; +use crate::{LprsError, LprsResult}; pub mod cipher; mod validator; @@ -62,7 +62,7 @@ pub struct Password { impl Password { /// Encrypt the password data - pub fn encrypt(self, master_password: &[u8]) -> PassrsResult { + pub fn encrypt(self, master_password: &[u8]) -> LprsResult { Ok(Self { name: cipher::encrypt(master_password, &self.name)?, username: cipher::encrypt(master_password, &self.username)?, @@ -79,7 +79,7 @@ impl Password { } /// Decrypt the password data - pub fn decrypt(self, master_password: &[u8]) -> PassrsResult { + pub fn decrypt(self, master_password: &[u8]) -> LprsResult { Ok(Self { name: cipher::decrypt(master_password, &self.name)?, username: cipher::decrypt(master_password, &self.username)?, @@ -111,32 +111,32 @@ impl Passwords { } /// Encrypt the passwords - fn encrypt(self) -> PassrsResult { + fn encrypt(self) -> LprsResult { Ok(Self { passwords: self .passwords .into_iter() .map(|p| p.encrypt(&self.master_password)) - .collect::>>()?, + .collect::>>()?, ..self }) } /// Reload the passwords from the file - pub fn try_reload(passwords_file: PathBuf, master_password: Vec) -> PassrsResult { + pub fn try_reload(passwords_file: PathBuf, master_password: Vec) -> LprsResult { let passwords = serde_json::from_str::>(&fs::read_to_string(&passwords_file)?)? .into_iter() .map(|p| p.decrypt(master_password.as_slice())) - .collect::>>()?; + .collect::>>()?; Ok(Self::new(master_password, passwords_file, passwords)) } /// Export the passwords to the file - pub fn try_export(self) -> PassrsResult<()> { + pub fn try_export(self) -> LprsResult<()> { let path = self.passwords_file.to_path_buf(); - fs::write(path, serde_json::to_string(&self.encrypt()?.passwords)?).map_err(PassrsError::Io) + fs::write(path, serde_json::to_string(&self.encrypt()?.passwords)?).map_err(LprsError::Io) } /// Add new password diff --git a/src/password/validator.rs b/src/password/validator.rs index ec21fd4..c4f28ea 100644 --- a/src/password/validator.rs +++ b/src/password/validator.rs @@ -16,12 +16,12 @@ use std::{fs, path::Path}; -use crate::PassrsResult; +use crate::LprsResult; use super::Password; /// Return if the password file new file or not -pub fn is_new_password_file(path: &Path) -> PassrsResult { +pub fn is_new_password_file(path: &Path) -> LprsResult { if path.exists() { let file_content = fs::read_to_string(path)?; if !file_content.is_empty() diff --git a/src/traits.rs b/src/traits.rs index 8183d13..e3a1c5e 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -14,9 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::{password::Passwords, PassrsResult}; +use crate::{password::Passwords, LprsResult}; /// Trait to run the command pub trait RunCommand { - fn run(&self, password_manager: Passwords) -> PassrsResult<()>; + fn run(&self, password_manager: Passwords) -> LprsResult<()>; } diff --git a/src/utils.rs b/src/utils.rs index bf0c205..fbb9525 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -16,11 +16,11 @@ use std::{fs, path::PathBuf}; -use crate::{PassrsError, PassrsResult}; +use crate::{LprsError, LprsResult}; /// Return the default passwords json file -pub fn passwords_file() -> PassrsResult { - if let Some(path) = directories::ProjectDirs::from("", "", "passrs") +pub fn passwords_file() -> LprsResult { + if let Some(path) = directories::ProjectDirs::from("", "", "lprs") .map(|d| d.data_local_dir().to_path_buf().join("passwords.json")) { if let Some(parent) = path.parent() { @@ -31,7 +31,7 @@ pub fn passwords_file() -> PassrsResult { } Ok(path) } else { - Err(PassrsError::ProjectDir( + Err(LprsError::ProjectDir( "Can't extract the project_dir from this OS".to_owned(), )) }