use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader, Write};
enum FileHandle {
Read(BufReader<File>),
Write(File),
}
pub struct IoContext {
handles: HashMap<i32, FileHandle>,
next_fd: i32,
}
impl Default for IoContext {
fn default() -> Self {
Self::new()
}
}
impl IoContext {
pub fn new() -> Self {
Self {
handles: HashMap::new(),
next_fd: 3,
}
}
pub fn fopen(&mut self, path: &str, mode: &str) -> i32 {
let handle = match mode {
"r" => File::open(path).map(|f| FileHandle::Read(BufReader::new(f))),
"w" => File::create(path).map(FileHandle::Write),
"a" => OpenOptions::new()
.append(true)
.create(true)
.open(path)
.map(FileHandle::Write),
"r+" => OpenOptions::new()
.read(true)
.write(true)
.open(path)
.map(FileHandle::Write),
_ => return -1,
};
match handle {
Ok(h) => {
let fd = self.next_fd;
self.handles.insert(fd, h);
self.next_fd += 1;
fd
}
Err(_) => -1,
}
}
pub fn fclose(&mut self, fd: i32) -> i32 {
if self.handles.remove(&fd).is_some() {
0
} else {
-1
}
}
pub fn fclose_all(&mut self) {
self.handles.clear();
}
pub fn fgetl(&mut self, fd: i32) -> Option<String> {
match self.handles.get_mut(&fd)? {
FileHandle::Read(reader) => {
let mut line = String::new();
match reader.read_line(&mut line) {
Ok(0) => None,
Ok(_) => {
if line.ends_with('\n') {
line.pop();
}
if line.ends_with('\r') {
line.pop();
}
Some(line)
}
Err(_) => None,
}
}
FileHandle::Write(_) => None,
}
}
pub fn fgets(&mut self, fd: i32) -> Option<String> {
match self.handles.get_mut(&fd)? {
FileHandle::Read(reader) => {
let mut line = String::new();
match reader.read_line(&mut line) {
Ok(0) => None,
Ok(_) => Some(line),
Err(_) => None,
}
}
FileHandle::Write(_) => None,
}
}
pub fn write_to_fd(&mut self, fd: i32, s: &str) -> Result<(), String> {
match fd {
1 => {
print!("{s}");
std::io::stdout().flush().ok();
Ok(())
}
2 => {
eprint!("{s}");
std::io::stderr().flush().ok();
Ok(())
}
_ => match self.handles.get_mut(&fd) {
Some(FileHandle::Write(f)) => f
.write_all(s.as_bytes())
.map_err(|e| format!("fprintf: write error: {e}")),
Some(FileHandle::Read(_)) => {
Err(format!("fprintf: fd {fd} is not open for writing"))
}
None => Err(format!("fprintf: invalid file descriptor {fd}")),
},
}
}
}