Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ tempfile = "3.20.0"
serde = { version = "1.0", features = ["derive"] }
up_finder = "0.0.4"
dirs = "6.0.0"
editor-command = "1.0.0"
editor-command = "1.0.0"
globset = "0.4.16"
1 change: 1 addition & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ clap = "4.5.41"
thiserror.workspace = true
miette = { version = "7.6.0", features = ["fancy"] }
editor-command.workspace = true
globset.workspace = true

[dev-dependencies]
rstest.workspace = true
Expand Down
3 changes: 3 additions & 0 deletions cli/src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
pub mod config;
pub mod ignore;
pub mod init;
pub mod meva_command;

pub use config::ConfigCommand;
pub use ignore::IgnoreCommand;
pub use init::InitCommand;

pub use meva_command::MevaCommand;
46 changes: 5 additions & 41 deletions cli/src/commands/config/subcommands/edit.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use clap::{ArgMatches, Command};
use editor_command::EditorBuilder;
use engine::{ConfigDocument, ConfigLoader};
use miette::{Context, IntoDiagnostic};

use engine::{ConfigDocument, ConfigLoader};

use crate::commands::MevaCommand;
use crate::extensions::{LocationSelection, WithLocations};
use crate::extensions::{LocationSelection, OpenInEditor, WithLocations};

/// Implements the `edit` subcommand for Meva configuration management.
///
Expand Down Expand Up @@ -43,7 +43,7 @@ impl MevaCommand for ConfigEditCommand {

fn execute(&self, matches: &ArgMatches) -> miette::Result<()> {
let loader = ConfigLoader::new_default();
let override_cmd = loader.get("core.editor", None);
let override_cmd = loader.get("core.editor", None).ok();
let location = matches
.get_config_location()
.get_default_path()
Expand All @@ -52,43 +52,7 @@ impl MevaCommand for ConfigEditCommand {

ConfigDocument::validate_existing_file(&location).into_diagnostic()?;

let mut builder = EditorBuilder::new().environment();

if let Ok(editor) = override_cmd {
if !editor.trim().is_empty() {
builder = builder.source(Some(editor));
}
} else {
#[cfg(target_os = "windows")]
{
use std::env;
let has_editor = env::var_os("VISUAL").is_some() || env::var_os("EDITOR").is_some();
if !has_editor {
builder = builder.source(Some("notepad".to_string()));
}
}
}

let mut cmd = builder
.build()
.into_diagnostic()
.wrap_err("Failed to build editor command")?;

cmd.arg(location);

let status = cmd
.status()
.into_diagnostic()
.wrap_err("Failed to launch the editor")?;

if !status.success() {
return Err(miette::miette!(
"Editor returned error code: {}",
status.code().unwrap_or(-1)
));
}

Ok(())
location.open_in_editor(override_cmd)
}
}

Expand Down
61 changes: 61 additions & 0 deletions cli/src/commands/ignore.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
pub mod subcommands;

use crate::commands::MevaCommand;

use subcommands::*;

/// Implements the `ignore` command for Meva DVCS.
///
/// Serves as a namespace for subcommands managing ignore patterns:
/// add, remove, check, and edit ignore rules for files and directories.
pub struct IgnoreCommand;

impl IgnoreCommand {
pub fn new() -> Self {
Self
}
}

impl MevaCommand for IgnoreCommand {
fn name(&self) -> &'static str {
"ignore"
}

fn about(&self) -> &'static str {
"Manage ignore patterns for files and directories"
}

fn version(&self) -> &'static str {
"1.0.0"
}

/// Define the set of subcommands under `ignore` namespace.
///
/// Returns boxed instances of each ignore operation command.
fn subcommands(&self) -> Vec<Box<dyn MevaCommand>> {
vec![
Box::new(IgnoreAddCommand),
Box::new(IgnoreCheckCommand),
Box::new(IgnoreEditCommand),
Box::new(IgnoreRemoveCommand),
]
}
}

#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use rstest::rstest;

#[rstest]
fn test_command_name_about_version() {
let cmd = IgnoreCommand::new();
assert_eq!(cmd.name(), "ignore");
assert_eq!(
cmd.about(),
"Manage ignore patterns for files and directories"
);
assert_eq!(cmd.version(), "1.0.0");
}
}
9 changes: 9 additions & 0 deletions cli/src/commands/ignore/subcommands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pub mod add;
pub mod check;
pub mod edit;
pub mod remove;

pub use add::IgnoreAddCommand;
pub use check::IgnoreCheckCommand;
pub use edit::IgnoreEditCommand;
pub use remove::IgnoreRemoveCommand;
77 changes: 77 additions & 0 deletions cli/src/commands/ignore/subcommands/add.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use std::path::PathBuf;

use clap::{ArgMatches, Command};
use engine::{IgnoreOperations, IgnoreService, MevaRepository, RepositoryLayout};
use globset::Glob;
use miette::IntoDiagnostic;

use crate::{
commands::MevaCommand,
extensions::{WithFile, WithPattern},
};

/// Implements the `add` subcommand for Meva ignored files management.
///
/// Adds the specified pattern to the chosen ignore file,
/// ensuring the ignore rule is appended without duplicates or formatting errors.
pub struct IgnoreAddCommand;

impl IgnoreAddCommand {
/// Creates a new instance of the `IgnoreAddCommand`.
#[allow(dead_code)]
pub fn new() -> Self {
Self
}
}

impl MevaCommand for IgnoreAddCommand {
fn name(&self) -> &'static str {
"add"
}

fn about(&self) -> &'static str {
"Appends a pattern to the ignore file"
}

fn version(&self) -> &'static str {
"1.0.0"
}

fn build_command(&self) -> Command {
self.build_base_command()
.with_pattern_arg("Pattern to append to the ignore file")
.with_file_arg("Path to a specific ignore file")
}

fn execute(&self, matches: &ArgMatches) -> miette::Result<()> {
let pattern = matches.get_one::<Glob>(Command::ARG_PATTERN).unwrap();
let file = matches.get_one::<PathBuf>(Command::ARG_FILE);

let ignore_service = IgnoreService::new(MevaRepository::IGNORE_FILE);

let result_path = ignore_service.add(pattern, file).into_diagnostic()?;

println!(
"Pattern '{}' appended to {}",
pattern,
result_path.to_string_lossy()
);

Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use rstest::rstest;

#[rstest]
fn test_command_name_about_version() {
let cmd = IgnoreAddCommand::new();
assert_eq!(cmd.name(), "add");
assert_eq!(cmd.about(), "Appends a pattern to the ignore file");
assert_eq!(cmd.version(), "1.0.0");
}
}
Loading