#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(unsafe_code)]
#![deny(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(static_mut_refs)]
#![deny(clippy::declare_interior_mutable_const)]
#![deny(clippy::borrow_interior_mutable_const)]
#![deny(clippy::redundant_clone)]
#![deny(clippy::ptr_arg)]
#![deny(clippy::needless_borrow)]
#![deny(clippy::cloned_instead_of_copied)]
#![deny(rustdoc::private_intra_doc_links)]
#![deny(rustdoc::invalid_html_tags)]
#![deny(rustdoc::bare_urls)]
#![deny(rustdoc::redundant_explicit_links)]
#![deny(rustdoc::unescaped_backticks)]
#![deny(rustdoc::invalid_rust_codeblocks)]
#![warn(clippy::doc_markdown)]
#![allow(clippy::collapsible_if)]
#![allow(clippy::needless_return)]
rust_i18n::i18n!("locales", fallback = "en");
pub mod atomic;
pub mod best_effort;
pub mod binary_detect;
pub mod checksum;
pub mod cli;
pub mod cli_args;
pub mod commands;
pub mod concurrency;
pub mod config;
pub mod constants;
pub mod env_detect;
pub mod error;
pub mod file_io;
pub mod fuzzy;
#[cfg(feature = "ast")]
#[cfg_attr(docsrs, doc(cfg(feature = "ast")))]
pub mod grammar;
#[cfg(feature = "ast")]
#[cfg_attr(docsrs, doc(cfg(feature = "ast")))]
pub mod lang_utils;
pub mod line_endings;
pub mod locale;
pub mod lock;
pub mod media;
pub mod ndjson;
#[allow(deprecated)]
pub mod ndjson_types {
pub use crate::ndjson::*;
}
pub mod output;
pub mod path_safety;
pub mod platform;
pub mod retry;
pub mod runtime;
pub mod signal;
pub mod stdin_cancel;
pub mod storage;
#[cfg(feature = "ast")]
#[cfg_attr(docsrs, doc(cfg(feature = "ast")))]
pub mod syntax_check;
#[cfg(not(feature = "ast"))]
#[path = "syntax_check_stub.rs"]
pub mod syntax_check;
pub mod wal;
pub mod walk_globs;
pub mod xattr_restore;
use std::io::{Read, Write};
use anyhow::Result;
use crate::cli::{Cli, Commands};
use crate::error::AtomwriteError;
use crate::output::NdjsonWriter;
pub use crate::cli_args::FuzzyMode;
pub use crate::fuzzy::{FuzzyInfo, match_pair};
pub use crate::ndjson_types::BestCandidate;
include!("schema_dispatch.inc.rs");
include!("lib_dispatch.inc.rs");
pub fn run(
cli: &Cli,
stdin: impl Read + Send + 'static,
stdout: impl Write,
stdin_is_tty: bool,
) -> Result<()> {
if cli.global.json_schema {
return emit_json_schema(&cli.command, stdout);
}
if cli.global.print_schema.is_some() {
return Err(crate::error::AtomwriteError::InvalidInput {
reason: "`--print-schema` must precede the command name; placed after a command it \
would replace that command with a schema dump. Run `atomwrite --print-schema` \
(no command) to list every schema, or `atomwrite --print-schema NAME` to emit one"
.to_string(),
}
.into());
}
if let Commands::Completions(args) = &cli.command {
return generate_completions(args, stdout);
}
if cli.global.json {
tracing::debug!("--json is a no-op; output is always NDJSON");
}
crate::output::arm_limit(cli.global.limit);
crate::path_safety::set_follow_symlinks(cli.global.follow_symlinks);
let shape = crate::output::OutputShape::from_global_args(&cli.global)?;
let mut writer = NdjsonWriter::with_shape(stdout, shape);
let shutdown = signal::get_or_install_handlers()?;
signal::arm_global_timeout(cli.global.timeout_secs);
let workspace = cli.global.resolve_workspace()?;
writer.set_workspace(if cli.global.workspace.is_some() {
"argv"
} else {
"cwd"
});
let mut stdin = stdin_cancel::CancellableStdin::new(stdin, shutdown.flag());
writer.load_joins(
&mut stdin,
stdin_is_tty,
crate::output::command_consumes_stdin(&cli.command),
)?;
if let Commands::Config(args) = &cli.command {
let result = run_config_verb(args, &cli.global, &mut writer, &workspace);
return finish_run(result, &mut writer, &shutdown);
}
let config = crate::config::load_config(&workspace, cli.global.config.as_deref())?;
crate::config::validate_all(&config)?;
writer.set_profile(crate::output::OutputProfile::from_name(
&config.output.profile,
));
crate::config::install_tuning(&config);
concurrency::plan_global_pool(&cli.command, cli.global.threads);
startup_wal_heal(&cli.command, &workspace, cli.global.no_auto_heal);
if let Commands::Batch(args) = &cli.command
&& args.input_schema
{
return commands::batch::emit_input_schema(&mut writer);
}
let result = dispatch_command(
cli,
stdin,
stdin_is_tty,
&mut writer,
&shutdown,
&workspace,
&config,
);
finish_run(result, &mut writer, &shutdown)
}
fn generate_completions(args: &cli::CompletionsArgs, mut out: impl Write) -> Result<()> {
let shell = match args.shell {
cli::ShellType::Bash => clap_complete::Shell::Bash,
cli::ShellType::Zsh => clap_complete::Shell::Zsh,
cli::ShellType::Fish => clap_complete::Shell::Fish,
cli::ShellType::PowerShell => clap_complete::Shell::PowerShell,
cli::ShellType::Elvish => clap_complete::Shell::Elvish,
};
if args.install {
let xdg_data = crate::storage::data_dir()
.and_then(|d| d.parent().map(|p| p.to_path_buf()))
.or_else(crate::storage::data_dir)
.ok_or_else(|| anyhow::anyhow!("cannot determine XDG data directory (storage)"))?;
let (subdir, filename) = match args.shell {
cli::ShellType::Bash => crate::constants::COMPLETION_BASH,
cli::ShellType::Zsh => crate::constants::COMPLETION_ZSH,
cli::ShellType::Fish => crate::constants::COMPLETION_FISH,
cli::ShellType::PowerShell => crate::constants::COMPLETION_POWERSHELL,
cli::ShellType::Elvish => crate::constants::COMPLETION_ELVISH,
};
let install_dir = xdg_data.join(subdir);
std::fs::create_dir_all(&install_dir)
.map_err(|e| AtomwriteError::from_io_at(&install_dir, e))?;
let install_path = install_dir.join(filename);
let mut file = std::fs::File::create(&install_path)
.map_err(|e| AtomwriteError::from_io_at(&install_path, e))?;
file.write_all(&render_completions(shell))
.map_err(|e| AtomwriteError::from_io_at(&install_path, e))?;
let event = CompletionsInstalled {
r#type: "installed",
path: install_path.display().to_string(),
};
let mut writer = NdjsonWriter::new(out);
writer.set_target(event.path.clone(), "argv");
writer.write_event(&event)?;
writer.flush()?;
Ok(())
} else {
out.write_all(&render_completions(shell))?;
out.flush()?;
Ok(())
}
}
fn render_completions(shell: clap_complete::Shell) -> Vec<u8> {
use clap::CommandFactory;
let mut script = Vec::new();
clap_complete::generate(shell, &mut cli::Cli::command(), "atomwrite", &mut script);
script
}