Basic prompt/command stuff
Some checks failed
Rust / Test (push) Failing after 8s
Rust / Rustfmt (push) Failing after 3s
Rust / Clippy (push) Failing after 6s
Rust / Code coverage (push) Failing after 21s
Security audit / security_audit (push) Failing after -4s

This commit is contained in:
2025-11-12 05:43:10 -05:00
parent 081683b955
commit 5f42831461
4 changed files with 152 additions and 2 deletions

104
src/command.rs Normal file
View File

@@ -0,0 +1,104 @@
use std::io::{self, BufRead, ErrorKind, Read, Seek, Write};
use thiserror::Error;
#[derive(Debug, Copy, Default, Clone, PartialEq, Eq)]
pub enum Encoding {
#[default]
UTF8Strict,
UTF8Lossy,
Bytes,
}
#[derive(Debug, Copy, Default, Clone, PartialEq, Eq)]
pub enum LineEnding {
Keep,
StripLF,
#[default]
StripCRLF,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ReadOptions {
pub encoding: Encoding,
pub ending: LineEnding,
pub max_read: u64,
}
impl Default for ReadOptions {
#[inline(always)]
fn default() -> Self {
Self {
encoding: Default::default(),
ending: Default::default(),
max_read: 2048,
}
}
}
pub struct Command {
pub line: String,
}
#[derive(Debug, Error)]
pub enum CommandReadError {
#[error("An IO Error occurred while attempting to retrieve the next command.")]
IOError(#[from] io::Error),
#[error(
"The maximum line length limit was encountered while attempting to read the next command."
)]
MaxCommandLengthError(String),
#[error("The command input was closed.")]
InputClosed,
}
#[inline]
fn normalize_line_ending(v: &mut String, ending: LineEnding) {
if v.ends_with('\n') {
match ending {
LineEnding::StripLF => {
v.pop();
}
LineEnding::StripCRLF => {
v.pop();
if v.ends_with('\r') {
v.pop();
}
}
_ => {
println!("No match? {}", v.ends_with('\n'));
}
}
}
}
/// Read a single line of `source` into `sink` using the provided ReadOptions.
pub fn read_command(opts: ReadOptions) -> Result<Option<String>, CommandReadError> {
match opts.encoding {
Encoding::UTF8Strict => {}
encode => unimplemented!(
"Encoding support for {:?} has not been implemented yet!",
encode
),
}
let mut line = String::with_capacity(8);
let stdin = io::stdin().lock();
let n = stdin.take(opts.max_read).read_line(&mut line)?;
if n == 0 {
return Err(CommandReadError::InputClosed);
}
if n == (opts.max_read as usize) && !line.ends_with('\n') {
return Err(CommandReadError::MaxCommandLengthError(line));
}
normalize_line_ending(&mut line, opts.ending);
if line.is_empty() {
Ok(None)
} else {
Ok(Some(line))
}
}

View File

@@ -1,3 +1,6 @@
pub mod command;
pub mod prompt;
use strum::Display;
// Terminal Colors

View File

@@ -1,3 +1,32 @@
fn main() {
println!("Hello, world!");
use std::io::Write;
use cacoon_lib::{command::{read_command, CommandReadError, ReadOptions}, prompt::write_prompt};
use anyhow::Result;
fn main() -> Result<()> {
let mut stdout = std::io::stdout().lock();
let prompt = "Cacoon -> ";
let response = "Haha, you said: ".as_bytes();
loop {
write_prompt(prompt)?;
match read_command(ReadOptions::default()) {
Ok(Some(command)) => {
// Laugh at the user
stdout.write_all(response)?;
stdout.write_all(command.as_bytes())?;
stdout.write_all(b"\n")?;
stdout.flush()?;
}
Err(CommandReadError::InputClosed) => {
stdout.write_all(b"\nGoodbye.\n")?;
stdout.flush()?;
return Ok(());
},
other => {
other?;
},
}
}
}

14
src/prompt.rs Normal file
View File

@@ -0,0 +1,14 @@
use std::io::{self, stdout, Write};
/// Writes the provided prompt string into the provided output.
pub fn write_prompt_into<W: Write>(prompt: &str, output: &mut W) -> io::Result<()> {
output.write_all(prompt.as_bytes())?;
output.flush()?;
Ok(())
}
/// Writes the provided prompt to stdout.
pub fn write_prompt(prompt: &str, ) -> io::Result<()> {
let mut stdout = stdout().lock();
write_prompt_into(prompt, &mut stdout)
}