diff --git a/Cargo.lock b/Cargo.lock index 058ce4e..54d877b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,6 +63,17 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "atomicwrites" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7b2dbe9169059af0f821e811180fddc971fc210c776c133c7819ccd6e478db" +dependencies = [ + "rustix", + "tempfile", + "windows-sys 0.52.0", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -142,6 +153,22 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + [[package]] name = "filetime" version = "0.2.25" @@ -209,6 +236,7 @@ name = "jsoncutil" version = "0.1.0" dependencies = [ "anyhow", + "atomicwrites", "clap", "fjson", "notify-debouncer-mini", @@ -251,6 +279,12 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "log" version = "0.4.22" @@ -299,6 +333,12 @@ dependencies = [ "notify", ] +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + [[package]] name = "proc-macro2" version = "1.0.86" @@ -326,6 +366,19 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "rustix" +version = "0.38.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "same-file" version = "1.0.6" @@ -352,6 +405,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/Cargo.toml b/Cargo.toml index 1fd0aff..690fbde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] anyhow = "1.0.86" +atomicwrites = "0.4.3" clap = { version = "4.5.16", features = ["derive"] } fjson = "0.3.1" notify-debouncer-mini = "0.4.1" diff --git a/src/main.rs b/src/main.rs index f46cc1e..52b134b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use anyhow::{bail, Context, Result}; +use atomicwrites::{AtomicFile, OverwriteBehavior::DisallowOverwrite}; use clap::{Args, Parser, Subcommand}; use notify_debouncer_mini::{new_debouncer, notify::*, DebounceEventResult}; use std::{ @@ -69,46 +70,34 @@ fn format_single_file(args: &FmtArgs) -> Result<()> { return Ok(()); } - if let Some(ref output) = args.output { - let mut output_file = fs::OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .open(output) - .context("Opening output file")?; - + if let Some(ref output_file) = args.output { let output = fjson::to_jsonc(&input_str).context("Formatting to jsonc")?; - output_file - .write_all(&output.as_bytes()) + AtomicFile::new(output_file, DisallowOverwrite) + .write(|f| f.write_all(&output.as_bytes())) .context("Writing jsonc output")?; - - output_file.flush().context("Flushing jsonc output")?; } - if let Some(ref json_output) = args.json_output { - let mut json_output_file = fs::OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .open(json_output) - .context("Opening output file")?; - + if let Some(ref json_output_file) = args.json_output { let output = if args.compact { fjson::to_json(&input_str).context("Formatting to json") } else { fjson::to_json_compact(&input_str).context("Formatting to json") }?; - json_output_file - .write_all(&output.as_bytes()) - .context("Writing json output")?; - json_output_file.flush().context("Flushing json output")?; + AtomicFile::new(json_output_file, DisallowOverwrite) + .write(|f| f.write_all(&output.as_bytes())) + .context("Writing jsonc output")?; } Ok(()) } +// let af = AtomicFile::new("foo", DisallowOverwrite); +// af.write(|f| { +// f.write_all(b"HELLO") +// })?; + // fn x() { // // Select recommended watcher for debouncer. // // Using a callback here, could also be a channel.