• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cdprice02 / ferrish / #3

08 Feb 2026 08:12PM UTC coverage: 43.243%. Remained the same
#3

push

cdprice02
Add first integration tests

4 of 9 new or added lines in 3 files covered. (44.44%)

3 existing lines in 2 files now uncovered.

112 of 259 relevant lines covered (43.24%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/shell.rs
1
use std::cell::RefCell;
2

3
use crate::{
4
    Command,
5
    arg::Args,
6
    error::ShellResult,
7
    executor,
8
    exit::ExitCode,
9
    io::{ShellIo, StandardIo},
10
    parser,
11
};
12

13
pub struct Shell<IO: ShellIo + ?Sized> {
14
    io: RefCell<IO>,
15
}
16

17
pub type StandardShell = Shell<StandardIo>;
18

19
impl Shell<dyn ShellIo> {
20
    pub const fn prefix() -> &'static str {
21
        "\u{1F980}> " // 🦀>
22
    }
23
    /// Create a shell builder
24
    ///
25
    /// # Example
26
    /// ```
27
    /// use ferrish::Shell;
28
    ///
29
    /// let mut shell = Shell::builder()
30
    ///     .with_std_io();
31
    /// ```
32
    pub fn builder() -> ShellBuilder {
33
        ShellBuilder
34
    }
35
}
36

37
impl<IO: ShellIo> Shell<IO> {
38
    pub fn io(&self) -> std::cell::Ref<'_, IO> {
×
39
        self.io.borrow()
×
40
    }
41

42
    pub fn io_mut(&'_ mut self) -> std::cell::RefMut<'_, IO> {
×
43
        self.io.borrow_mut()
×
44
    }
45

46
    pub fn run(&mut self) -> anyhow::Result<()> {
×
47
        loop {
×
48
            self.io
×
49
                .borrow_mut()
50
                .out_writer()
NEW
51
                .write_all(Shell::prefix().as_bytes())?;
×
52

53
            let mut buffer = Vec::<u8>::new();
×
54
            let bytes = self.io.borrow_mut().read_line(&mut buffer)?;
×
55
            if bytes == 0 {
×
56
                continue;
×
57
            }
58

59
            let buffer = buffer.trim_ascii();
×
60
            if buffer.is_empty() {
×
61
                continue;
×
62
            }
63

64
            let (command, args) = parser::parse(buffer);
×
65
            match self.execute_command(command.clone(), args) {
×
NEW
66
                Ok(Some(_exit_code)) => {
×
67
                    // TODO: set exit code in caller env
UNCOV
68
                    break;
×
69
                }
70
                Ok(None) => {}
×
71
                Err(e) => {
×
72
                    let fatal = e.is_fatal();
×
73
                    let e = anyhow::Error::new(e).context(command);
×
74
                    writeln!(self.io.borrow_mut().err_writer(), "{:#}", e)?;
×
75

76
                    if fatal {
×
77
                        return Err(e);
×
78
                    }
79
                }
80
            }
81
        }
82

83
        Ok(())
×
84
    }
85

86
    pub fn run_script(&mut self, script: &[&str]) -> anyhow::Result<ExitCode> {
×
87
        for line in script {
×
88
            let buffer = line.as_bytes();
×
89
            let buffer = buffer.trim_ascii();
×
90

91
            if buffer.is_empty() {
×
92
                // TODO: handle comments
93
                continue;
×
94
            }
95

96
            let (command, args) = parser::parse(buffer);
×
97
            if let Some(exit_code) = self.execute_command(command, args)? {
×
98
                // TODO: set exit code in caller env instead of returning
99
                return Ok(exit_code);
×
100
            }
101
        }
102

103
        Ok(ExitCode::SUCCESS)
×
104
    }
105

106
    pub fn execute_command(
×
107
        &mut self,
108
        command: Command,
109
        args: Args,
110
    ) -> ShellResult<Option<ExitCode>> {
111
        executor::execute(command, args, &mut *self.io.borrow_mut())
×
112
    }
113
}
114

115
pub struct ShellBuilder;
116

117
impl ShellBuilder {
118
    /// Configure the shell with standard I/O (stdin/stdout/stderr)
119
    ///
120
    /// # Example
121
    /// ```
122
    /// use ferrish::Shell;
123
    ///
124
    /// let mut shell = Shell::builder()
125
    ///     .with_std_io();
126
    /// ```
127
    pub fn with_std_io(self) -> Shell<StandardIo> {
×
128
        Shell {
129
            io: RefCell::new(StandardIo::default()),
×
130
        }
131
    }
132

133
    /// Configure the shell with custom I/O
134
    ///
135
    /// # Example
136
    /// ```
137
    /// use ferrish::Shell;
138
    /// use ferrish::io::MockIo;
139
    ///
140
    /// let io = MockIo::from_lines(&["echo test", "exit"]);
141
    /// let mut shell = Shell::builder()
142
    ///     .with_io(io);
143
    /// ```
144
    pub fn with_io<IO: ShellIo>(self, io: IO) -> Shell<IO> {
×
145
        Shell {
146
            io: RefCell::new(io),
×
147
        }
148
    }
149
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc