use std::fs;
use std::io::{self, Read};
use std::sync::Arc;
use anyhow::anyhow;
use clap::ValueEnum;
use eure::query::{
CacheOptions, Glob, GlobResult, TextFile, TextFileContent, fetch_url, fetch_url_cached,
};
use eure::query_flow::{DurabilityLevel, Query, QueryError, QueryRuntime};
use eure::report::{ErrorReports, format_error_reports};
pub fn read_input(file: Option<&str>) -> Result<String, String> {
match file {
None | Some("-") => {
let mut buffer = String::new();
io::stdin()
.read_to_string(&mut buffer)
.map_err(|e| format!("Error reading from stdin: {e}"))?;
Ok(buffer)
}
Some(path) => fs::read_to_string(path).map_err(|e| format!("Error reading file: {e}")),
}
}
pub fn display_path(file: Option<&str>) -> &str {
file.unwrap_or("<stdin>")
}
#[derive(ValueEnum, Clone, Debug)]
pub enum VariantFormat {
External,
Internal,
Adjacent,
Untagged,
}
pub fn handle_query_error(runtime: &QueryRuntime, e: QueryError) -> ! {
if let Some(reports) = e.downcast_ref::<ErrorReports>() {
eprintln!(
"{}",
format_error_reports(runtime, reports, true).expect("file content should be loaded")
);
} else {
eprintln!("Error: {e}");
}
std::process::exit(1);
}
pub fn run_query_with_file_loading_cached<Q, R>(
runtime: &QueryRuntime,
query: Q,
cache_opts: Option<&CacheOptions>,
) -> Result<Arc<R>, QueryError>
where
Q: Query<Output = R> + Clone,
{
loop {
match runtime.query(query.clone()) {
Ok(result) => return Ok(result),
Err(QueryError::Suspend { .. }) => {
for pending in runtime.pending_assets() {
if let Some(file) = pending.key::<TextFile>() {
match file {
TextFile::Local(path) => {
let path_ref = path.as_ref();
let content = fs::read_to_string(path_ref).map_err(|e| {
std::io::Error::new(
e.kind(),
format!("{}: {}", path_ref.display(), e),
)
})?;
runtime.resolve_asset(
file.clone(),
TextFileContent(content),
DurabilityLevel::Static,
);
}
TextFile::Remote(url) => {
let result = if let Some(opts) = cache_opts {
fetch_url_cached(url, opts)
} else {
fetch_url(url)
};
match result {
Ok(content) => {
runtime.resolve_asset(
file.clone(),
content,
DurabilityLevel::Static,
);
}
Err(e) => {
runtime.resolve_asset_error::<TextFile>(
file.clone(),
anyhow!("Failed to fetch {}: {}", url, e),
DurabilityLevel::Static,
);
}
}
}
}
} else if let Some(glob_key) = pending.key::<Glob>() {
let pattern = glob_key.full_pattern();
let pattern_str = pattern.to_string_lossy();
let files: Vec<TextFile> = glob::glob(&pattern_str)
.into_iter()
.flat_map(|paths| paths.flatten().map(TextFile::from_path))
.collect();
runtime.resolve_asset(
glob_key.clone(),
GlobResult(files),
DurabilityLevel::Static,
);
}
}
}
Err(e) => return Err(e),
}
}
}