forked from rescript-lang/rescript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathformat.rs
More file actions
128 lines (109 loc) · 4.04 KB
/
format.rs
File metadata and controls
128 lines (109 loc) · 4.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use crate::{helpers, project_context};
use anyhow::{Result, bail};
use num_cpus;
use rayon::prelude::*;
use std::fs;
use std::io::{self, Write};
use std::path::Path;
use std::process::Command;
use std::sync::atomic::{AtomicUsize, Ordering};
use crate::build::packages;
use crate::cli::FileExtension;
use clap::ValueEnum;
pub fn format(stdin_extension: Option<FileExtension>, check: bool, files: Vec<String>) -> Result<()> {
let bsc_path = helpers::get_bsc();
match stdin_extension {
Some(extension) => {
format_stdin(&bsc_path, extension)?;
}
None => {
let files = if files.is_empty() {
get_files_in_scope()?
} else {
files
};
format_files(&bsc_path, files, check)?;
}
}
Ok(())
}
fn get_files_in_scope() -> Result<Vec<String>> {
let current_dir = std::env::current_dir()?;
let project_context = project_context::ProjectContext::new(¤t_dir)?;
let packages = packages::make(&None, &project_context, false)?;
let mut files: Vec<String> = Vec::new();
let packages_to_format = project_context.get_scoped_local_packages();
for (_package_name, package) in packages {
if packages_to_format.contains(&package.name)
&& let Some(source_files) = &package.source_files
{
for (path, _metadata) in source_files {
if let Some(extension) = path.extension()
&& (extension == "res" || extension == "resi")
{
files.push(package.path.join(path).to_string_lossy().into_owned());
}
}
}
}
Ok(files)
}
fn format_stdin(bsc_exe: &Path, extension: FileExtension) -> Result<()> {
let extension_value = extension
.to_possible_value()
.ok_or(anyhow::anyhow!("Could not get extension arg value"))?;
let mut temp_file = tempfile::Builder::new()
.suffix(extension_value.get_name())
.tempfile()?;
io::copy(&mut io::stdin(), &mut temp_file)?;
let temp_path = temp_file.path();
let mut cmd = Command::new(bsc_exe);
cmd.arg("-format").arg(temp_path);
let output = cmd.output()?;
if output.status.success() {
io::stdout().write_all(&output.stdout)?;
} else {
let stderr_str = String::from_utf8_lossy(&output.stderr);
bail!("Error formatting stdin: {}", stderr_str);
}
Ok(())
}
fn format_files(bsc_exe: &Path, files: Vec<String>, check: bool) -> Result<()> {
let batch_size = 4 * num_cpus::get();
let incorrectly_formatted_files = AtomicUsize::new(0);
files.par_chunks(batch_size).try_for_each(|batch| {
batch.iter().try_for_each(|file| {
let mut cmd = Command::new(bsc_exe);
// Always get formatted output to stdout for comparison
cmd.arg("-format").arg(file);
let output = cmd.output()?;
if output.status.success() {
let original_content = fs::read_to_string(file)?;
let formatted_content = String::from_utf8_lossy(&output.stdout);
if original_content != formatted_content {
if check {
eprintln!("[format check] {file}");
incorrectly_formatted_files.fetch_add(1, Ordering::SeqCst);
} else {
// Only write if content actually changed
fs::write(file, &*formatted_content)?;
}
}
} else {
let stderr_str = String::from_utf8_lossy(&output.stderr);
bail!("Error formatting {}: {}", file, stderr_str);
}
Ok(())
})
})?;
let count = incorrectly_formatted_files.load(Ordering::SeqCst);
if count > 0 {
if count == 1 {
eprintln!("The file listed above needs formatting");
} else {
eprintln!("The {count} files listed above need formatting");
}
bail!("Formatting check failed");
}
Ok(())
}