Test Failed, removing unused lines (Rust)

Test Failed, removing unused lines (Rust)

use std::io::{self, BufWriter, Write};
use std::iter::{Enumerate, Peekable};
use std::process::Stdio;
use std::str::Chars;
use std::{borrow::Cow, fmt, fs, path::PathBuf, process, str::FromStr};

fn main() -> io::Result<()> {
    let stdin = io::stdin();
    print!("$ ");
    io::stdout().flush()?;

    for line in stdin.lines() {
        let line = line?;
        if line.trim().is_empty() {
            print!("$ ");
            io::stdout().flush()?;
            continue;
        }
        let (redirect_path, args) = get_redirect_path(IterArgs::new(line.as_str()).collect())?;
        let cmd = Cmd::from(args);
        cmd.execute(redirect_path)?;
        print!("$ ");
        io::stdout().flush()?;
    }
    Ok(())
}

#[derive(Debug, PartialEq, Eq)]
enum Cmd<'a> {
    Exit(i32),
    Echo(Vec<Cow<'a, str>>),
    Type(Cow<'a, str>),
    Pwd,
    Cd(Cow<'a, str>),
    Other(Cow<'a, str>, Vec<Cow<'a, str>>),
}

impl fmt::Display for Cmd<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Exit(_) => f.write_str("exit")?,
            Self::Echo(_) => f.write_str("echo")?,
            Self::Type(_) => f.write_str("type")?,
            Self::Pwd => f.write_str("pwd")?,
            Self::Cd(_) => f.write_str("cd")?,
            Self::Other(cmd, _) => {
                if let Some(path) = find_path(cmd) {
                    return write!(f, "{} is {}", cmd, path);
                } else {
                    return write!(f, "{}: not found", cmd);
                }
            }
        };
        f.write_str(" is a shell builtin")
    }
}

impl Cmd<'_> {
    fn is_builtin(&self) -> bool {
        !matches!(self, Self::Other(_, _))
    }
}

impl<'a> Cmd<'a> {
    fn execute(&'a self, out: Redirection<'_>) -> io::Result<()> {
        let mut stdout = BufWriter::new(out.stdout()?);
        let mut stderr = BufWriter::new(out.stderr()?);
        match self {
            Self::Exit(code) => std::process::exit(*code),
            Self::Echo(args) => {
                let mut iter = args.iter();
                if let Some(arg) = iter.next() {
                    write!(stdout, "{}", arg)?;
                }
                for arg in iter {
                    write!(stdout, " {}", arg)?;
                }
                writeln!(stdout)?;
            }
            Self::Type(arg) => {
                let arg = match arg {
                    Cow::Owned(v) => v,
                    Cow::Borrowed(v) => *v,
                };
                let cmd = Self::from(arg);
                if cmd.is_builtin() {
                    writeln!(stdout, "{}", cmd)?;
                    return Ok(());
                }
                if let Some(v) = find_path(arg) {
                    writeln!(stdout, "{} is {}", arg, v)?;
                    return Ok(());
                }
                writeln!(stdout, "{}: not found", arg)?;
            }
            Self::Pwd => {
                let pwd = std::env::current_dir()?;
                writeln!(stdout, "{}", pwd.to_string_lossy())?;
            }
            Self::Cd(path) => {
                if *path == "~" {
                    let home = std::env::var("HOME").unwrap();
                    std::env::set_current_dir(home)?;
                } else if std::env::set_current_dir(PathBuf::from_str(path).unwrap()).is_err() {
                    writeln!(stdout, "cd: {}: No such file or directory", path)?;
                }
            }
            Self::Other(cmd, args) => {
                if find_path(cmd).is_some() {
                    let mut child = process::Command::new(cmd.as_ref())
                        .args(args.iter().map(|v| v.as_ref()).collect::<Vec<&str>>())
                        .stdout(Stdio::from(out.stdout()?))
                        .stderr(Stdio::from(out.stderr()?))
                        .spawn()?;
                    let _ = child.wait()?;
                } else {
                    writeln!(stdout, "{}: command not found", cmd)?;
                }
            }
        }
        Ok(())
    }
}

impl<'a> From<&'a str> for Cmd<'a> {
    fn from(value: &'a str) -> Self {
        let value = value.trim_start();
        let mut cmd_args = IterArgs::new(value);
        let cmd = cmd_args.next().unwrap();
        match cmd.as_ref() {
            "exit" => {
                let code = cmd_args.next().unwrap_or_default();
                Self::Exit(code.parse().unwrap_or_default())
            }
            "echo" => Self::Echo(cmd_args.collect()),
            "type" => Self::Type(cmd_args.next().unwrap_or_default()),
            "pwd" => Self::Pwd,
            "cd" => Self::Cd(cmd_args.next().unwrap_or(Cow::Borrowed("~"))),
            _ => Self::Other(cmd, cmd_args.collect()),
        }
    }
}
impl<'a> From<Vec<Cow<'a, str>>> for Cmd<'a> {
    fn from(value: Vec<Cow<'a, str>>) -> Self {
        let mut iter = value.into_iter();
        let cmd = iter.next().unwrap();
        match cmd.as_ref() {
            "exit" => {
                let code = iter.next().unwrap_or_default();
                Self::Exit(code.parse().unwrap_or_default())
            }
            "echo" => Self::Echo(iter.collect()),
            "type" => Self::Type(iter.next().unwrap_or_default()),
            "pwd" => Self::Pwd,
            "cd" => Self::Cd(iter.next().unwrap_or(Cow::Borrowed("~"))),
            _ => Self::Other(cmd, iter.collect()),
        }
    }
}
fn find_path<T: AsRef<str>>(value: T) -> Option<String> {
    let env = std::env::var("PATH").unwrap();
    for path in env.split(':') {
        for entry in fs::read_dir(path).ok()? {
            let dir = entry.ok()?;
            let file = dir.file_name();
            let name = file.to_string_lossy();
            if name == *value.as_ref() {
                return Some(dir.path().to_string_lossy().to_string());
            }
        }
    }
    None
}

struct IterArgs<'a> {
    whole: &'a str,
    start: usize,
}

impl<'a> Iterator for IterArgs<'a> {
    type Item = Cow<'a, str>;
    fn next(&mut self) -> Option<Self::Item> {
        loop {
            if self.start >= self.whole.len() {
                return None;
            }
            let input = &self.whole[self.start..];
            let mut end = 0;
            let mut rm = Vec::new();
            handle_args(&mut input.chars().enumerate().peekable(), &mut rm, &mut end);
            let got_str = remove_unwanted(&input[0..end], rm);
            self.start += end;
            if got_str.is_empty() && end >= self.whole.len() {
                return None;
            }
            if got_str.is_empty() {
                continue;
            }
            return Some(got_str);
        }
    }
}
impl<'a> IterArgs<'a> {
    fn new(value: &'a str) -> Self {
        Self {
            whole: value,
            start: 0,
        }
    }
}

// BUG: in some input it return Owned value, when it should be Borrowed
fn remove_unwanted(value: &str, remove: Vec<usize>) -> Cow<'_, str> {
    if remove.is_empty() || value.is_empty() {
        return Cow::Borrowed(value);
    }
    let mut start = 0;
    for i in remove.iter() {
        if *i != start {
            break;
        }
        start += 1;
    }
    let mut end = value.len() - 1;
    for item in remove.iter().rev() {
        if *item != end {
            break;
        }
        end -= 1;
    }
    if start + (value.len() - 1 - end) >= value.len() {
        return Cow::Borrowed("");
    }
    if start + (value.len() - 1 - end) >= remove.len() {
        return Cow::Borrowed(&value[start..end + 1]);
    }
    let mut st = String::with_capacity(end - start + 1);
    let mut remove_iter = remove[start..].iter();
    let mut current_remove = remove_iter.next();
    for (index, c) in value[start..end + 1].chars().enumerate() {
        match current_remove {
            Some(remove_index) if *remove_index == index + start => {
                current_remove = remove_iter.next();
            }
            _ => st.push(c),
        }
    }
    Cow::Owned(st)
}
fn handle_args(iter: &mut Peekable<Enumerate<Chars>>, remove: &mut Vec<usize>, end: &mut usize) {
    if iter.peek().is_none() {
        return;
    }
    let mut i = 0;
    while let Some((index, c)) = iter.next() {
        i = index;
        match c {
            ' ' | '\t' | '\r' => {
                remove.push(index);
                *end = index + 1;
                return;
            }
            '\\' => {
                remove.push(index);
                iter.next();
                i += 1;
            }
            '"' => {
                remove.push(index);
                while let Some((ii, v)) = iter.next() {
                    i = ii;
                    match v {
                        '"' => {
                            remove.push(ii);
                            break;
                        }
                        '\\' => {
                            if let Some((_, v)) = iter.peek() {
                                if matches!(*v, '\\' | '"') {
                                    remove.push(ii);
                                    iter.next();
                                }
                            }
                        }
                        _ => {}
                    }
                }
            }
            '\'' => {
                remove.push(index);
                for (ii, v) in iter.by_ref() {
                    i = ii;
                    if v == '\'' {
                        remove.push(ii);
                        break;
                    }
                }
            }
            _ => {}
        }
    }
    *end = i + 1;
}

#[derive(Debug)]
enum RedirOps {
    Redirect,
    Append,
}

#[derive(Debug)]
struct RedirectPath<'a> {
    path: Cow<'a, str>,
    ops: RedirOps,
}

impl RedirectPath<'_> {
    fn default_stdout() -> Self {
        Self {
            path: Cow::Borrowed("/dev/stdout"),
            ops: RedirOps::Append,
        }
    }
    fn default_stderr() -> Self {
        RedirectPath {
            path: Cow::Borrowed("/dev/stderr"),
            ops: RedirOps::Append,
        }
    }
}

#[derive(Debug)]
struct Redirection<'a> {
    std_out: RedirectPath<'a>,
    std_err: RedirectPath<'a>,
}

impl Default for Redirection<'_> {
    fn default() -> Self {
        Self {
            std_out: RedirectPath::default_stdout(),
            std_err: RedirectPath::default_stderr(),
        }
    }
}

impl Redirection<'_> {
    fn stdout(&self) -> io::Result<fs::File> {
        match self.std_out.ops {
            RedirOps::Append => Ok(fs::OpenOptions::new()
                .append(true)
                .create(true)
                .open(self.std_out.path.as_ref())?),
            RedirOps::Redirect => Ok(fs::File::create(self.std_out.path.as_ref())?),
        }
    }
    fn stderr(&self) -> io::Result<fs::File> {
        match self.std_err.ops {
            RedirOps::Append => Ok(fs::OpenOptions::new()
                .append(true)
                .create(true)
                .open(self.std_err.path.as_ref())?),
            RedirOps::Redirect => Ok(fs::File::create(self.std_err.path.as_ref())?),
        }
    }
}

fn get_redirect_path(args: Vec<Cow<'_, str>>) -> io::Result<(Redirection<'_>, Vec<Cow<'_, str>>)> {
    let mut args1 = Vec::with_capacity(args.len());
    let mut iter = args.into_iter();
    let mut stdout_path = None;
    let mut stdout_ops = RedirOps::Append;
    let mut stderr_path = None;
    let mut stderr_ops = RedirOps::Append;
    while let Some(arg) = iter.next() {
        match arg.as_ref() {
            ">" | "1>" => {
                if stdout_path.is_none() {
                    stdout_path = iter.next();
                    stdout_ops = RedirOps::Redirect;
                }
            }
            ">>" | "1>>" => {
                if stderr_path.is_none() {
                    stdout_path = iter.next();
                }
            }
            "2>" => {
                if stderr_path.is_none() {
                    stderr_path = iter.next();
                    stderr_ops = RedirOps::Redirect;
                }
            }
            "2>>" => {
                if stderr_path.is_none() {
                    stderr_path = iter.next();
                }
            }
            _ => args1.push(arg),
        }
    }
    Ok((
        Redirection {
            std_out: RedirectPath {
                path: stdout_path.unwrap_or(Cow::Borrowed("/dev/stdout")),
                ops: stdout_ops,
            },
            std_err: RedirectPath {
                path: stderr_path.unwrap_or(Cow::Borrowed("/dev/stderr")),
                ops: stderr_ops,
            },
        },
        args1,
    ))
}

Test Result of above code

eagle@hp-lp:~/development/codecrafters-shell-rust$ codecrafters test
Initiating test run...

⚡ This is a turbo test run. https://codecrafters.io/turbo

Running tests. Logs should appear shortly...

[compile]    Compiling codecrafters-shell v0.1.0 (/app)
[compile] warning: unused variable: `stderr`
[compile]   --> src/main.rs:67:17
[compile]    |
[compile] 67 |         let mut stderr = BufWriter::new(out.stderr()?);
[compile]    |                 ^^^^^^ help: if this is intentional, prefix it with an underscore: `_stderr`
[compile]    |
[compile]    = note: `#[warn(unused_variables)]` on by default
[compile] 
[compile] warning: variable does not need to be mutable
[compile]   --> src/main.rs:67:13
[compile]    |
[compile] 67 |         let mut stderr = BufWriter::new(out.stderr()?);
[compile]    |             ----^^^^^^
[compile]    |             |
[compile]    |             help: remove this `mut`
[compile]    |
[compile]    = note: `#[warn(unused_mut)]` on by default
[compile] 
[compile] warning: `codecrafters-shell` (bin "codecrafters-shell") generated 2 warnings (run `cargo fix --bin "codecrafters-shell"` to apply 1 suggestion)
[compile]     Finished `release` profile [optimized] target(s) in 2.42s
[compile] Moved ./.codecrafters/run.sh → ./your_program.sh
[compile] Compilation successful.
.....
.....
.....
.....

[tester::#CZ2] Running tests for Stage #CZ2 (Handle invalid commands)
[tester::#CZ2] Running ./your_program.sh
[your-program] $ invalid_blueberry_command
[your-program] invalid_blueberry_command: command not found
[tester::#CZ2] ✓ Received command not found message
[your-program] $ 
[tester::#CZ2] Test passed.

[tester::#OO8] Running tests for Stage #OO8 (Print a prompt)
[tester::#OO8] Running ./your_program.sh
[your-program] $ 
[tester::#OO8] ✓ Received prompt
[tester::#OO8] Test passed.

Test passed. Congrats!

Submit your changes to move to the next step:

$ codecrafters submit
eagle@hp-lp:~/development/codecrafters-shell-rust$ 

When I remove unused lines (no: 67).

        let mut stderr = BufWriter::new(out.stderr()?);

The test is failed

eagle@hp-lp:~/development/codecrafters-shell-rust$ cargo clippy
    Checking codecrafters-shell v0.1.0 (/home/eagle/development/codecrafters-shell-rust)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
eagle@hp-lp:~/development/codecrafters-shell-rust$ codecrafters test
Initiating test run...

⚡ This is a turbo test run. https://codecrafters.io/turbo

Running tests. Logs should appear shortly...

[compile]    Compiling codecrafters-shell v0.1.0 (/app)
[compile]     Finished `release` profile [optimized] target(s) in 2.32s
[compile] Moved ./.codecrafters/run.sh → ./your_program.sh
[compile] Compilation successful.

[tester::#UN3] Running tests for Stage #UN3 (Redirection - Append stderr)
[tester::#UN3] Running ./your_program.sh
[tester::#UN3] Writing file "/tmp/bar/apple" with content "apple"
[tester::#UN3] Writing file "/tmp/bar/grape" with content "grape"
[tester::#UN3] Writing file "/tmp/bar/raspberry" with content "raspberry"
[your-program] $ ls nonexistent >> /tmp/quz/bar.md
[your-program] ls: cannot access 'nonexistent': No such file or directory
[tester::#UN3] ✓ Received error message
[tester::#UN3] ✓ File: /tmp/quz/bar.md is empty
[your-program] $ ls nonexistent 2>> /tmp/quz/qux.md
[your-program] $ cat /tmp/quz/qux.md
[your-program] ls: cannot access 'nonexistent': No such file or directory
[tester::#UN3] ✓ Received redirected file content
[your-program] $ echo "David says Error" 2>> /tmp/quz/quz.md
[your-program] David says Error
[tester::#UN3] ✓ Received redirected file content
[your-program] $ cat nonexistent 2>> /tmp/quz/quz.md
[your-program] $ ls nonexistent 2>> /tmp/quz/quz.md
[your-program] $ cat /tmp/quz/quz.md
[your-program] cat: nonexistent: No such file or directory
[your-program] ls: cannot access 'nonexistent': No such file or directory
[tester::#UN3] ✓ Received redirected file content
[your-program] $ 
[tester::#UN3] Test passed.

[tester::#EL9] Running tests for Stage #EL9 (Redirection - Append stdout)
[tester::#EL9] Running ./your_program.sh
[tester::#EL9] Writing file "/tmp/bar/banana" with content "banana"
[tester::#EL9] Writing file "/tmp/bar/mango" with content "mango"
[tester::#EL9] Writing file "/tmp/bar/pineapple" with content "pineapple"
[your-program] $ ls /tmp/bar >> /tmp/baz/bar.md
[your-program] $ cat /tmp/baz/bar.md
[your-program] banana
[your-program] mango
[your-program] pineapple
[tester::#EL9] ✓ Received redirected file content
[your-program] $ echo 'Hello Alice' 1>> /tmp/baz/baz.md
[your-program] $ echo 'Hello Alice' 1>> /tmp/baz/baz.md
[your-program] $ cat /tmp/baz/baz.md
[your-program] Hello Alice
[your-program] Hello Alice
[tester::#EL9] ✓ Received redirected file content
[your-program] $ echo "List of files: " > /tmp/baz/qux.md
[your-program] $ ls /tmp/bar >> /tmp/baz/qux.md
[your-program] $ cat /tmp/baz/qux.md
[your-program] List of files:
[your-program] banana
[your-program] mango
[your-program] pineapple
[tester::#EL9] ✓ Received redirected file content
[your-program] $ 
[tester::#EL9] Test passed.

[tester::#VZ4] Running tests for Stage #VZ4 (Redirection - Redirect stderr)
[tester::#VZ4] Running ./your_program.sh
[tester::#VZ4] Writing file "/tmp/foo/mango" with content "mango"
[your-program] $ ls nonexistent 2> /tmp/bar/bar.md
[your-program] $ cat /tmp/bar/bar.md
[your-program] ls: cannot access 'nonexistent': No such file or directory
[tester::#VZ4] ✓ Received redirected error message
[your-program] $ echo 'Emily file cannot be found' 2> /tmp/bar/baz.md
[tester::#VZ4] Failed to read file ("/tmp/bar/baz.md"): open /tmp/bar/baz.md: no such file or directory
[your-program] Emily file cannot be found
[your-program] $ 
[tester::#VZ4] Assertion failed.
[tester::#VZ4] Test failed (try setting 'debug: true' in your codecrafters.yml to see more details)

View our article on debugging test failures: https://codecrafters.io/debug
eagle@hp-lp:~/development/codecrafters-shell-rust$ 

Hi @BiswajitThakur, could you upload your code to GitHub and share the link? It will be much easier to debug if I can run it directly.

Could you confirm if you’re still encountering the issue? I couldn’t reproduce the warning with your code.

I have already completed all the changes but there is a unused variable in my code which I already provided if you remove the unused variable Append stderr #un3 doesn’t pass all the test.

I was removed the unused variable and I manually test including Append stderr. Everything works well, there is no issue.

@BiswajitThakur Congratulations on completing all the stages—great work! :tada:

I ran the code you provided but wasn’t able to reproduce warning: unused variable: stderr. Could you confirm if you’re still seeing the warnings, or share more details or clarify the steps you took?

If you remove line #[allow(unused)] (line no: 65), you can see the the worning massage.

Got it! It seems the code doesn’t work correctly when let mut stderr = BufWriter::new(out.stderr()?) is deleted.

  • Without let mut stderr ..., “maria.error” is not created:

  • With let mut stderr ... untouched, “maria.error” is successfully created:

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.