What is the best variant for appending a new line in a text file? - file-io

I am using this code to append a new line to the end of a file:
let text = "New line".to_string();
let mut option = OpenOptions::new();
option.read(true);
option.write(true);
option.create(true);
match option.open("foo.txt") {
Err(e) => {
println!("Error");
}
Ok(mut f) => {
println!("File opened");
let size = f.seek(SeekFrom::End(0)).unwrap();
let n_text = match size {
0 => text.clone(),
_ => format!("\n{}", text),
};
match f.write_all(n_text.as_bytes()) {
Err(e) => {
println!("Write error");
}
Ok(_) => {
println!("Write success");
}
}
f.sync_all();
}
}
It works, but I think it's too difficult. I found option.append(true);, but if I use it instead of option.write(true); I get "Write error".

Using OpenOptions::append is the clearest way to append to a file:
use std::fs::OpenOptions;
use std::io::prelude::*;
fn main() {
let mut file = OpenOptions::new()
.write(true)
.append(true)
.open("my-file")
.unwrap();
if let Err(e) = writeln!(file, "A new line!") {
eprintln!("Couldn't write to file: {}", e);
}
}
As of Rust 1.8.0 (commit) and RFC 1252, append(true) implies write(true). This should not be a problem anymore.
Before Rust 1.8.0, you must use both write and append — the first one allows you to write bytes into a file, the second specifies where the bytes will be written.

Related

Is there a way to get an OS error code from a std::io::Error?

When I run the following:
use std::fs::File;
fn main() {
let filename = "not_exists.txt";
let reader = File::open(filename);
match reader {
Ok(_) => println!(" * file '{}' opened successfully.", filename),
Err(e) => {
println!("{:?}", &e);
}
}
}
The output is:
Os { code: 2, kind: NotFound, message: "No such file or directory" }
Is it possible to get that code as an integer?
Use io::Error::raw_os_error:
match reader {
Ok(_) => println!(" * file '{}' opened successfully.", filename),
Err(e) => println!("{:?}", e.raw_os_error()),
}
Output:
Some(2)
See also:
How does one get the error message as provided by the system without the "os error n" suffix?
Yes, use the raw_os_error method on std::io::Error. Example:
use std::fs::File;
fn main() {
let filename = "not_exists.txt";
let reader = File::open(filename);
match reader {
Ok(_) => println!(" * file '{}' opened successfully.", filename),
Err(e) => {
println!("{:?} {:?}", e, e.raw_os_error());
}
}
}
playground

Modified Rust Book Guessing Game Query

I modified the code from the Rust Book's Guessing Game Tutorial to make it a little shorter; for a slide. Alas, I've introduced a bug, and it no longer executes correctly: the first input works as expected, but subsequent entries now yield no feedback.
What is the best way to guard against this situation?
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
let mut guess = String::new();
loop {
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}
read_line will append the line to buffer, so your guess will accumulate all the inputs include newline characters! Moving let mut guess = String::new(); inside the loop solves the problem:
fn main() {
...
loop {
let mut guess = String::new();
...
}
}

How to chain functions that return a result and return if any of them fail? [duplicate]

This question already has answers here:
Returning default value from function when result is error
(3 answers)
How do you unwrap a Result on Ok or return from the function on Err?
(4 answers)
Closed 3 years ago.
Is there any way to express the following code in a more concise manner? The function should only continue if the result is Ok(), else it should return a default object. The code I came up with looks kind of unidiomatic, so I wondered if there is a better and more concise way.
fn get_config() -> Config {
let cfg = match get_config_file_path() { // can return a (custom) PathError
Ok(c) => c,
Err(e) => {
error(&*format!(
"{} -> Using default configuration",
e.description
));
return Config::default();
}
};
let cfg_str = match fs::read_to_string(&cfg) { // io::Error
Ok(c) => c,
Err(_) => {
error("Failed to read config file");
return Config::default();
}
};
toml::from_str(&*cfg_str).unwrap_or_else(|_| { // toml::de::Error
error("Failed to parse config file");
Config::default()
})
}
Edit: My solution:
I created the following macro:
macro_rules! unwrap_or_return {
( $e:expr, $c:expr) => {
match $e {
Ok(x) => x,
Err(_) => return $c,
}
};
}
fn get_config() -> Config {
let cfg = unwrap_or_return!(get_config_file_path(), {
error("Failed to get path to configuration file");
Config::default()
});
let cfg_str = unwrap_or_return!(fs::read_to_string(&cfg), {
error("Failed to read config file");
Config::default()
});
toml::from_str(&*cfg_str).unwrap_or_else(|_| {
error("Failed to parse config file");
Config::default()
})
}

Save File with original filename when using the actix-web-Framework to upload a File on a webserver in Rust

I'm creating a webserver in Rust using the actix-web Framework. Currently I'm working on the Fileupload and for this im using actix-multipart.
In the official Actix-Documentation there is an example for it:
use std::cell::Cell;
use std::fs;
use std::io::Write;
use actix_multipart::{Field, Multipart, MultipartError};
use actix_web::{error, middleware, web, App, Error, HttpResponse, HttpServer};
use futures::future::{err, Either};
use futures::{Future, Stream};
pub fn save_file(field: Field) -> impl Future<Item = i64, Error = Error> {
let file_path_string = "upload.png";
let file = match fs::File::create(file_path_string) {
Ok(file) => file,
Err(e) => return Either::A(err(error::ErrorInternalServerError(e))),
};
Either::B(
field
.fold((file, 0i64), move |(mut file, mut acc), bytes| {
// fs operations are blocking, we have to execute writes
// on threadpool
web::block(move || {
file.write_all(bytes.as_ref()).map_err(|e| {
println!("file.write_all failed: {:?}", e);
MultipartError::Payload(error::PayloadError::Io(e))
})?;
acc += bytes.len() as i64;
Ok((file, acc))
})
.map_err(|e: error::BlockingError<MultipartError>| {
match e {
error::BlockingError::Error(e) => e,
error::BlockingError::Canceled => MultipartError::Incomplete,
}
})
})
.map(|(_, acc)| acc)
.map_err(|e| {
println!("save_file failed, {:?}", e);
error::ErrorInternalServerError(e)
}),
)
}
pub fn upload(
multipart: Multipart,
counter: web::Data<Cell<usize>>,
) -> impl Future<Item = HttpResponse, Error = Error> {
counter.set(counter.get() + 1);
println!("{:?}", counter.get());
multipart
.map_err(error::ErrorInternalServerError)
.map(|field| save_file(field).into_stream())
.flatten()
.collect()
.map(|sizes| HttpResponse::Ok().json(sizes))
.map_err(|e| {
println!("failed: {}", e);
e
})
}
fn index() -> HttpResponse {
let html = r#"<html>
<head><title>Upload Test</title></head>
<body>
<form target="/" method="post" enctype="multipart/form-data">
<input type="file" name="file"/>
<input type="submit" value="Submit"></button>
</form>
</body>
</html>"#;
HttpResponse::Ok().body(html)
}
fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.data(Cell::new(0usize))
.wrap(middleware::Logger::default())
.service(
web::resource("/")
.route(web::get().to(index))
.route(web::post().to_async(upload)),
)
})
.bind("127.0.0.1:8080")?
.run()
}
This would be a minimal working implementation for it and works good so far. But as you can see the filepathstring is a custom string which renames the file on the server to upload.png (let file_path_string = "upload.png")
So is there a simple way to retrieve the original filename and use it as filename for the uploaded file on the server?
The content_disposition() method as suggested by NK is potentially what you are after here. So you could perhaps replace:
let file_path_string = "upload.png";
with something like:
let file_path_string = match field.content_disposition().unwrap().get_filename() {
Some(filename) => filename.replace(' ', "_").to_string(),
None => return Either::A(err(error::ErrorInternalServerError("Couldn't read the filename.")))
}

How do I iterate over a Vec of functions returning Futures in Rust?

Is it possible to loop over a Vec, calling a method that returns a Future on each, and build a chain of Futures, to be evaluated (eventually) by the consumer? Whether to execute the later Futures would depend on the outcome of the earlier Futures in the Vec.
To clarify:
I'm working on an application that can fetch data from an arbitrary set of upstream sources.
Requesting data would check with each of the sources, in turn. If the first source had an error (Err), or did not have the data available (None), then the second source would be tried, and so on.
Each source should be tried exactly once, and no source should be tried until all of the sources before have returned their results. Errors are logged, but otherwise ignored, passing the query to the next upstream data source.
I have some working code that does this for fetching metadata:
/// Attempts to read/write data to various external sources. These are
/// nested types, because a data source may exist as both a reader and a writer
struct StoreManager {
/// Upstream data sources
readers: Vec<Rc<RefCell<StoreRead>>>,
/// Downstream data sinks
writers: Vec<Rc<RefCell<StoreWrite>>>,
}
impl StoreRead for StoreManager {
fn metadata(self: &Self, id: &Identifier) -> Box<Future<Option<Metadata>, Error>> {
Box::new(ok(self.readers
.iter()
.map(|store| {
executor::block_on(store.borrow().metadata(id)).unwrap_or_else(|err| {
error!("Error on metadata(): {:?}", err);
None
})
})
.find(Option::is_some)
.unwrap_or(None)))
}
}
Aside from my unhappiness with all of the Box and Rc/RefCell nonsense, my real concern is with the executor::block_on() call. It blocks, waiting for each Future to return a result, before continuing to the next.
Given that it's possible to call fn_returning_future().or_else(|_| other_fn()) and so on, is it possible to build up a dynamic chain like this? Or is it a requirement to fully evaluate each Future in the iterator before moving to the next?
You can use stream::unfold to convert a single value into a stream. In this case, we can use the IntoIter iterator as that single value.
use futures::{executor, stream, Stream, TryStreamExt}; // 0.3.4
type Error = Box<dyn std::error::Error>;
type Result<T, E = Error> = std::result::Result<T, E>;
async fn network_request(val: i32) -> Result<i32> {
// Just for demonstration, don't do this in a real program
use std::{
thread,
time::{Duration, Instant},
};
thread::sleep(Duration::from_secs(1));
println!("Resolving {} at {:?}", val, Instant::now());
Ok(val * 100)
}
fn requests_in_sequence(vals: Vec<i32>) -> impl Stream<Item = Result<i32>> {
stream::unfold(vals.into_iter(), |mut vals| async {
let val = vals.next()?;
let response = network_request(val).await;
Some((response, vals))
})
}
fn main() {
let s = requests_in_sequence(vec![1, 2, 3]);
executor::block_on(async {
s.try_for_each(|v| async move {
println!("-> {}", v);
Ok(())
})
.await
.expect("An error occurred");
});
}
Resolving 1 at Instant { tv_sec: 6223328, tv_nsec: 294631597 }
-> 100
Resolving 2 at Instant { tv_sec: 6223329, tv_nsec: 310839993 }
-> 200
Resolving 3 at Instant { tv_sec: 6223330, tv_nsec: 311005834 }
-> 300
To ignore Err and None, you have to shuttle the Error over to the Item, making the Item type a Result<Option<T>, Error>:
use futures::{executor, stream, Stream, StreamExt}; // 0.3.4
type Error = Box<dyn std::error::Error>;
type Result<T, E = Error> = std::result::Result<T, E>;
async fn network_request(val: i32) -> Result<Option<i32>> {
// Just for demonstration, don't do this in a real program
use std::{
thread,
time::{Duration, Instant},
};
thread::sleep(Duration::from_secs(1));
println!("Resolving {} at {:?}", val, Instant::now());
match val {
1 => Err("boom".into()), // An error
2 => Ok(None), // No data
_ => Ok(Some(val * 100)), // Success
}
}
fn requests_in_sequence(vals: Vec<i32>) -> impl Stream<Item = Result<Option<i32>>> {
stream::unfold(vals.into_iter(), |mut vals| async {
let val = vals.next()?;
let response = network_request(val).await;
Some((response, vals))
})
}
fn main() {
executor::block_on(async {
let s = requests_in_sequence(vec![1, 2, 3]);
let s = s.filter_map(|v| async move { v.ok() });
let s = s.filter_map(|v| async move { v });
let mut s = s.boxed_local();
match s.next().await {
Some(v) => println!("First success: {}", v),
None => println!("No successful requests"),
}
});
}
Resolving 1 at Instant { tv_sec: 6224229, tv_nsec: 727216392 }
Resolving 2 at Instant { tv_sec: 6224230, tv_nsec: 727404752 }
Resolving 3 at Instant { tv_sec: 6224231, tv_nsec: 727593740 }
First success: 300
is it possible to build up a dynamic chain like this
Yes, by leveraging async functions:
use futures::executor; // 0.3.4
type Error = Box<dyn std::error::Error>;
type Result<T, E = Error> = std::result::Result<T, E>;
async fn network_request(val: i32) -> Result<Option<i32>> {
// Just for demonstration, don't do this in a real program
use std::{
thread,
time::{Duration, Instant},
};
thread::sleep(Duration::from_secs(1));
println!("Resolving {} at {:?}", val, Instant::now());
match val {
1 => Err("boom".into()), // An error
2 => Ok(None), // No data
_ => Ok(Some(val * 100)), // Success
}
}
async fn requests_in_sequence(vals: Vec<i32>) -> Result<i32> {
let mut vals = vals.into_iter().peekable();
while let Some(v) = vals.next() {
match network_request(v).await {
Ok(Some(v)) => return Ok(v),
Err(e) if vals.peek().is_none() => return Err(e),
Ok(None) | Err(_) => { /* Do nothing and try the next source */ }
}
}
Err("Ran out of sources".into())
}
fn main() {
executor::block_on(async {
match requests_in_sequence(vec![1, 2, 3]).await {
Ok(v) => println!("First success: {}", v),
Err(e) => println!("No successful requests: {}", e),
}
});
}
See also:
Creating Diesel.rs queries with a dynamic number of .and()'s
is it a requirement to fully evaluate each Future in the iterator before moving to the next
Isn't that part of your own requirements? Emphasis mine:
Requesting data would check with each of the sources, in turn. If the first source had an error (Err), or did not have the data available (None), then the second source would be tried