use std::fs::{self, File};
use std::io::{self, Read};
use std::path::{Path, PathBuf};
use crate::canonical::{CanonicalError, CanonicalRequest, Content, ErrorKind, Message, Role};
use crate::pipeline::parse::parse;
pub fn open_input(path: Option<&Path>) -> io::Result<Box<dyn Read>> {
Ok(match path {
Some(path) => Box::new(File::open(path)?),
None => Box::new(io::stdin().lock()),
})
}
pub fn read_files(paths: &[PathBuf]) -> Result<Vec<Content>, (PathBuf, io::Error)> {
paths
.iter()
.map(|p| {
fs::read_to_string(p)
.map(Content::Text)
.map_err(|e| (p.clone(), e))
})
.collect()
}
pub fn read_request(
prompt: Option<&str>,
files: Vec<Content>,
reader: &mut dyn Read,
) -> Result<CanonicalRequest, CanonicalError> {
match prompt {
Some(prompt) => Ok(user_message(push_text(files, prompt))),
None if files.is_empty() => parse(reader),
None => {
let mut buf = Vec::new();
reader.read_to_end(&mut buf).map_err(read_err)?;
if buf.iter().any(|b| !b.is_ascii_whitespace()) {
Err(cannot_combine())
} else {
Ok(user_message(files))
}
}
}
}
fn push_text(mut parts: Vec<Content>, prompt: &str) -> Vec<Content> {
parts.push(Content::Text(prompt.to_owned()));
parts
}
fn user_message(content: Vec<Content>) -> CanonicalRequest {
CanonicalRequest {
messages: vec![Message {
role: Role::User,
content,
}],
..Default::default()
}
}
fn cannot_combine() -> CanonicalError {
CanonicalError {
kind: ErrorKind::Usage,
message: "cannot combine --file with a canonical request on stdin \
(put the file contents in the request's messages instead)"
.to_owned(),
provider_detail: None,
retry_after_seconds: None,
}
}
fn read_err(e: io::Error) -> CanonicalError {
CanonicalError {
kind: ErrorKind::ParseInput,
message: format!("failed to read stdin: {e}"),
provider_detail: None,
retry_after_seconds: None,
}
}