yash_env/io.rs
1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2021 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17//! Type definitions for I/O.
18
19use crate::Env;
20use crate::source::Location;
21use crate::source::pretty::{Report, ReportType, Snippet};
22#[cfg(doc)]
23use crate::system::SharedSystem;
24use annotate_snippets::Renderer;
25use std::borrow::Cow;
26#[cfg(unix)]
27use std::os::unix::io::RawFd;
28
29#[cfg(not(unix))]
30type RawFd = i32;
31
32/// File descriptor
33///
34/// This is the `newtype` pattern applied to [`RawFd`], which is merely a type
35/// alias.
36#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
37#[repr(transparent)]
38pub struct Fd(pub RawFd);
39
40impl Fd {
41 /// File descriptor for the standard input
42 pub const STDIN: Fd = Fd(0);
43 /// File descriptor for the standard output
44 pub const STDOUT: Fd = Fd(1);
45 /// File descriptor for the standard error
46 pub const STDERR: Fd = Fd(2);
47}
48
49impl From<RawFd> for Fd {
50 fn from(raw_fd: RawFd) -> Fd {
51 Fd(raw_fd)
52 }
53}
54
55impl std::fmt::Display for Fd {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 self.0.fmt(f)
58 }
59}
60
61/// Minimum file descriptor the shell may occupy for its internal use
62///
63/// POSIX reserves file descriptors below `MIN_INTERNAL_FD` so the user can use
64/// them freely. When the shell needs to open a file descriptor that is
65/// invisible to the user, it should be kept at `MIN_INTERNAL_FD` or above.
66/// (Hint: A typical way to move a file descriptor is to
67/// [`dup`](crate::system::System::dup) and
68/// [`close`](crate::system::System::close). You can also use
69/// [`move_fd_internal`].)
70///
71/// [`move_fd_internal`]: crate::system::SystemEx::move_fd_internal
72pub const MIN_INTERNAL_FD: Fd = Fd(10);
73
74/// Convenience function for converting a report into a string.
75///
76/// The returned string may contain ANSI color escape sequences if the given
77/// `env` allows it. The string will end with a newline.
78///
79/// To print the returned string to the standard error, you can use
80/// [`SharedSystem::print_error`].
81#[must_use]
82pub fn report_to_string(env: &Env, report: &Report<'_>) -> String {
83 let renderer = if env.should_print_error_in_color() {
84 Renderer::styled()
85 } else {
86 Renderer::plain()
87 };
88 format!("{}\n", renderer.render(&[report.into()]))
89}
90
91/// Convenience function for printing a report.
92///
93/// This function converts the `report` into a string by using
94/// [`report_to_string`], and prints the result to the standard error.
95pub async fn print_report(env: &mut Env, report: &Report<'_>) {
96 let report_str = report_to_string(env, report);
97 env.system.print_error(&report_str).await;
98}
99
100/// Convenience function for printing an error message.
101///
102/// This function constructs a temporary [`Report`] based on the given `title`,
103/// `label`, and `location`. The message is printed using [`print_report`].
104pub async fn print_error(
105 env: &mut Env,
106 title: Cow<'_, str>,
107 label: Cow<'_, str>,
108 location: &Location,
109) {
110 let mut report = Report::new();
111 report.r#type = ReportType::Error;
112 report.title = title;
113 report.snippets = Snippet::with_primary_span(location, label);
114 print_report(env, &report).await;
115}