how to return current working directory from function? - rust

has experience with high level programming languages. I read the Rust book and now trying to survive and understand how the "things" in Rust works. I would love that someone explain what the heck is - Ok(()) and how to deal with it? My goal is to return result from function in to the variable where the output:
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/rcp ./file/ aba`
Ok(
"/home/tomand/rcp",
)
Here is the full code:
use std::fs;
use std::env;
use serde_json;
use regex::Regex;
use std::path::Path;
fn determinate_file_size(file: &str) -> u64 {
fs::metadata(file).unwrap().len()
}
fn determinate_is_it_file_or_dirctory(arg: &str) -> &str {
let file = "File";
let dir = "Directory";
let re = Regex::new(r"/").unwrap();
if re.is_match(arg) {
return dir;
}
return file;
}
fn collect_user_arguments() -> Vec<String> {
env::args().collect()
}
fn check_if_arguments_count_valid(args: &Vec<String>) -> bool {
if args.len() == 3 {
return true
}
help();
return false
}
fn get_current_working_dir() -> Result<T> {
env::current_dir()
}
fn help() {
println!("Examples:");
println!("rcp [srcfile] [destfile]");
println!("rcp [srcdir]/[srcfile] [destdir]/[destfile]");
}
fn main() {
let WORKING_DIR = get_current_working_dir();
let args: Vec<String> = collect_user_arguments();
if check_if_arguments_count_valid(&args) {
let arg1 = &args[1];
let arg2 = &args[2];
println!("{:#?}", determinate_is_it_file_or_dirctory(&arg1));
}
}
Seems the compiler tried to give me some inspiration but eventually we miscommunicate in the end:
error[E0107]: this enum takes 2 generic arguments but 1 generic argument was supplied
--> src/main.rs:42:33
|
42 | fn get_current_working_dir() -> Result<T> {
| ^^^^^^ - supplied 1 generic argument
| |
| expected 2 generic arguments
EDIT:
I went with this approach:
fn get_current_working_dir() -> String {
let res = env::current_dir();
match res {
Ok(path) => path.into_os_string().into_string().unwrap(),
Err(_) => "FAILED".to_string()
}
}
It seems more practice is required to understand the Result type and how to manage it.

std::env::current_dir returns a std::io::Result<Pathbuf>, so you need to use that type in your wrapper method:
fn get_current_working_dir() -> std::io::Result<PathBuf> {
env::current_dir()
}
Playground
Other nitpick:
const is not a type so let WORKING_DIR: const = get_current_working_dir(); is wrong, just let WORKING_DIR = get_current_working_dir(); is enough.

Related

How to test the error message from anyhow::Error?

There is context that can convert an optional value to anyhow::Error which is very convenient.
Simplest way to unwrap an option and return Error if None (Anyhow)
However, how do we test that in unit-tests?
Let's say we have a foo like this:
fn foo(input: i32) -> Result<i32> {
// this only keep odd numbers
let filtered = if input % 2 == 0 { Some(input) } else { None };
filtered.context("Not a valid number")
}
It is easy to test that it is valid output, or that the output is an error. But how do we test the error message from the context?
mod test {
use super::*;
#[test]
fn test_valid_number() -> Result<()> {
let result = foo(4)?;
assert_eq!(result, 4);
Ok(())
}
#[test]
fn test_invalid_number() -> Result<()> {
let result = foo(3);
assert!(result.is_err());
Ok(())
}
// error[E0599]: no method named `message` found for struct `anyhow::Error` in the current scope
// --> src/main.rs:33:40
// |
// 33 | assert_eq!(result.unwrap_err().message(), "Not a valid number");
// | ^^^^^^^ method not found in `anyhow::Error`
#[test]
fn test_invalid_number_error_message() -> Result<()> {
let result = foo(3);
assert_eq!(result.unwrap_err().message(), "Not a valid number");
Ok(())
}
}
You can use .chain() and .root_cause() to deal with levels of context and use .downcast_ref() or format! to handle the specific error. For example, lets say you had 2 levels of context.
use anyhow::*;
fn bar(input: i32) -> Result<i32> {
// this only keep odd numbers
let filtered = if input % 2 == 0 { Some(input) } else { None };
filtered.context("Not a valid number")
}
fn foo(input: i32) -> Result<i32> {
return bar(input).context("Handled by bar")
}
In this example the chain would be of the errors "Handled by bar" -> "Not a valid number".
#[test]
fn check_top_error() -> Result<()> {
let result = foo(3);
let error = result.unwrap_err();
// Check top error or context
assert_eq!(format!("{}", error), "Handled by bar");
// Go down the error chain and inspect each error
let mut chain = error.chain();
assert_eq!(chain.next().map(|x| format!("{x}")), Some("Handled by bar".to_owned()));
assert_eq!(chain.next().map(|x| format!("{x}")), Some("Not a valid number".to_owned()));
assert_eq!(chain.next().map(|x| format!("{x}")), None);
Ok(())
}
#[test]
fn check_root_cause() -> Result<()> {
let result = foo(3);
let error = result.unwrap_err();
// Equivalent to .chain().next_back().unwrap()
let root_cause = error.root_cause();
assert_eq!(format!("{}", root_cause), "Not a valid number");
Ok(())
}
Now, you may have been wondering about my use of format!. It turns out a better solution exists involving downcast_ref, but it requires that your context implement std::error::Error and str does not. Here is an example of this taken directly from the anyhow documentation.
use anyhow::{Context, Result};
fn do_it() -> Result<()> {
helper().context(HelperFailed)?;
...
}
fn main() {
let err = do_it().unwrap_err();
if let Some(e) = err.downcast_ref::<HelperFailed>() {
// If helper failed, this downcast will succeed because
// HelperFailed is the context that has been attached to
// that error.
}
}
As a side note, you may find it easier to use .then() or .then_some() for cases like if input % 2 == 0 { Some(input) } else { None } where you create Some based on a boolean. Simply put, if abc { Some(xyz) } else { None } is equivalent to abc.then(|| xyz). .then_some() passes by value instead of using a closure so I don't usually use it.

Clashing types, crossterm::Result and core::Result error[E0107]:

I know the issue is that I have two Result types from different libraries but can't find how to fix it.
[dependencies]
crossterm = "0.23"
time = "0.3.9"
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.11", features = ["blocking", "json"] }
use time::Instant;
use std::collections::HashMap;
use crossterm::{
event::{self, Event, KeyCode, KeyEvent},
Result,
};
pub fn read_char() -> Result<char> {
loop {
if let Event::Key(KeyEvent {
code: KeyCode::Char(c),
..
}) = event::read()?
{
return Ok(c);
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let instant = Instant::now();
let response = reqwest::blocking::get("https://httpbin.org/ip")?
.json::<HashMap<String, String>>()?;
let duration = instant.elapsed();
println!("ns = {:?}, response: {:#?}, ", duration.whole_nanoseconds(), response);
// Any key to continue
println!("Press any key to continue:");
println!("{:?}", read_char());
Ok(())
}
Gives the error:
error[E0107]: this type alias takes 1 generic argument but 2 generic arguments were supplied
--> src\main.rs:20:14
|
20 | fn main() -> Result<(), Box<dyn std::error::Error>> {
| ^^^^^^ -------------------------- help: remove this generic argument
| |
| expected 1 generic argument
How do I fix this? I have searched but am likely looking for incorrect terms e.g. namespace alias and core::Result error[E0107] is not really helping.
I have tried this without success:
fn main() -> core::Result<(), Box<dyn std::error::Error>> {
You have crossterm ::Result in scope, so you would have to disambiguate the result you want to return, otherwise it just thinks you want to return the crossterm type:
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
...
Ok(())
}

How do I use PickleDB with Rocket/Juniper Context?

I'm trying to write a Rocket / Juniper / Rust based GraphQL Server using PickleDB - an in-memory key/value store.
The pickle db is created / loaded at the start and given to rocket to manage:
fn rocket() -> Rocket {
let pickle_path = var_os(String::from("PICKLE_PATH")).unwrap_or(OsString::from("pickle.db"));
let pickle_db_dump_policy = PickleDbDumpPolicy::PeriodicDump(Duration::from_secs(120));
let pickle_serialization_method = SerializationMethod::Bin;
let pickle_db: PickleDb = match Path::new(&pickle_path).exists() {
false => PickleDb::new(pickle_path, pickle_db_dump_policy, pickle_serialization_method),
true => PickleDb::load(pickle_path, pickle_db_dump_policy, pickle_serialization_method).unwrap(),
};
rocket::ignite()
.manage(Schema::new(Query, Mutation))
.manage(pickle_db)
.mount(
"/",
routes![graphiql, get_graphql_handler, post_graphql_handler],
)
}
And I want to retrieve the PickleDb instance from the Rocket State in my Guard:
pub struct Context {
pickle_db: PickleDb,
}
impl juniper::Context for Context {}
impl<'a, 'r> FromRequest<'a, 'r> for Context {
type Error = ();
fn from_request(_request: &'a Request<'r>) -> request::Outcome<Context, ()> {
let pickle_db = _request.guard::<State<PickleDb>>()?.inner();
Outcome::Success(Context { pickle_db })
}
}
This does not work because the State only gives me a reference:
26 | Outcome::Success(Context { pickle_db })
| ^^^^^^^^^ expected struct `pickledb::pickledb::PickleDb`, found `&pickledb::pickledb::PickleDb`
When I change my Context struct to contain a reference I get lifetime issues which I'm not yet familiar with:
15 | pickle_db: &PickleDb,
| ^ expected named lifetime parameter
I tried using 'static which does make rust quite unhappy and I tried to use the request lifetime (?) 'r of the FromRequest, but that does not really work either...
How do I get this to work? As I'm quite new in rust, is this the right way to do things?
I finally have a solution, although the need for unsafe indicates it is sub-optimal :)
#![allow(unsafe_code)]
use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::env;
use std::path::Path;
use std::time::Duration;
pub static mut PICKLE_DB: Option<PickleDb> = None;
pub fn cache_init() {
let pickle_path = env::var(String::from("PICKLE_PATH")).unwrap_or(String::from("pickle.db"));
let pickle_db_dump_policy = PickleDbDumpPolicy::PeriodicDump(Duration::from_secs(120));
let pickle_serialization_method = SerializationMethod::Json;
let pickle_db = match Path::new(&pickle_path).exists() {
false => PickleDb::new(
pickle_path,
pickle_db_dump_policy,
pickle_serialization_method,
),
true => PickleDb::load(
pickle_path,
pickle_db_dump_policy,
pickle_serialization_method,
)
.unwrap(),
};
unsafe {
PICKLE_DB = Some(pickle_db);
}
}
pub fn cache_get<V>(key: &str) -> Option<V>
where
V: DeserializeOwned + std::fmt::Debug,
{
unsafe {
let pickle_db = PICKLE_DB
.as_ref()
.expect("cache uninitialized - call cache_init()");
pickle_db.get::<V>(key)
}
}
pub fn cache_set<V>(key: &str, value: &V) -> Result<(), pickledb::error::Error>
where
V: Serialize,
{
unsafe {
let pickle_db = PICKLE_DB
.as_mut()
.expect("cache uninitialized - call cache_init()");
pickle_db.set::<V>(key, value)?;
Ok(())
}
}
This can be simply imported and used as expected, but I think I'll run into issues when the load gets to high...

Is it possible to compose a chain of functions at runtime?

I have 3 functions:
fn f1() -> u64 {
println!("Hello world: 1");
2
}
fn f2(i: u64) -> Box<FnMut()> {
println!("Hello world: {}", i);
Box::new(|| println!("Hello world: {}", 3))
}
fn f3(mut f: Box<FnMut()>) {
f()
}
One functional technique is chaining — connecting the output of function A to the input of function B:
fn main() {
f3(f2(f1()));
}
This may help in Rust because this method is purely functional and the functions can be pure functions; they don't touch global variables, only work with its arguments which were moved (which is awesome).
How can I do this chain at runtime? If I have function f4 which accepts the input of f2 but does not use it as f3. We can also use it for further chaining by adding a return type to it:
fn f4(_: Box<FnMut()>) -> bool {
println!("Hello world: 4");
true
}
fn main() {
f4(f2(f1())) // returns f4's result (true)
}
I want to be able to decide how to chain my functions at runtime. The example would be this Lua code (sorry for this):
function f1()
print("Hello world: 1")
return 2
end
function f2(args)
print("Hello world: " .. args)
return function()
print("Hello world: " .. args + 1)
end
end
function f3(args)
args()
end
function f4()
print("Hello world: 4")
end
function run_chain(list)
local args
for _, v in ipairs(list) do
args = v(args)
end
end
local list = {}
list[#list + 1] = f1
list[#list + 1] = f2
list[#list + 1] = f3
run_chain(list)
list[#list] = f4
run_chain(list)
This is a big plus of dynamic typing of scripting languages, but as far as I know Rust alleges that it is much more functional than C++ for example. Is it possible to chain the functions in such a way?
Here's how you can do the simple chaining issue. Converting it from free functions to a builder or operator style is left as an exercise. It also uses the "impl Trait" feature introduced in Rust 1.26 to make it nicer.
fn f1(_: ()) -> u64 {
println!("Hello world: 1");
2
}
fn f2(i: u64) -> Box<FnMut()> {
println!("Hello world: {}", i);
Box::new(|| println!("Hello world: {}", 3))
}
fn f3(mut f: Box<FnMut()>) {
f()
}
fn f4(_: Box<FnMut()>) -> bool {
println!("Hello world: 4");
true
}
fn dot<I, X, O, F1, F2>(mut f1: F1, mut f2: F2) -> impl FnMut(I) -> O
where
F1: FnMut(I) -> X,
F2: FnMut(X) -> O,
{
move |i| f2(f1(i))
}
fn main() {
let mut c = dot(dot(f1, f2), f3);
c(());
let mut c2 = dot(dot(f1, f2), f4);
c2(());
}
Playground
Gluing two functions together is not very hard, but you may run into lifetime issues if your types are more complex. In particular, if the input parameter to a function in the chain is a reference to the type that the previous function returns, this code will not compile. I believe that some more parameters and generic bounds can solve this issue, but you would have to experiment a bit.
See also the tool crate (compose is pretty much what I just posted) and the rustz crate, both of which add more functional idioms to Rust.
Edit: This answer is for the question as I originally understood it. The chaining comment at the end makes this answer not ideally; that's a different beast. The answer to that is, yes, it's possible, but like any metaprogramming not easy.
Not well, but this has nothing to do with being functional or not. It's about typing.
You can do this in Rust:
struct Chain {
f1: Box<FnMut() -> u64>,
f2: Box<FnMut(u64) -> Box<FnMut()>>,
f3: Box<FnMut(Box<FnMut()>)>,
}
impl Chain {
fn run(&self) {
f3(f2(f1()));
}
}
fn f1() -> u64 {
println!("Hello world: 1");
2
}
fn f2(i: u64) -> Box<FnMut()> {
println!("Hello world: {}", i);
Box::new(|| println!("Hello world: {}", 3))
}
fn f3(mut f: Box<FnMut()>) {
f()
}
fn main() {
let chain = Chain {
f1: Box::new(f1),
f2: Box::new(f2),
f3: Box::new(f3),
};
chain.run();
}
But you can't append arbitrary functions to this chain, nor can you substitute f4 for f3:
error[E0271]: type mismatch resolving `<fn(std::boxed::Box<std::ops::FnMut() + 'static>) -> bool {f4} as std::ops::FnOnce<(std::boxed::Box<std::ops::FnMut() + 'static>,)>>::Output == ()`
--> src/main.rs:36:13
|
36 | f3: Box::new(f4),
| ^^^^^^^^^^^^ expected bool, found ()
|
= note: expected type `bool`
found type `()`
= note: required for the cast to the object type `std::ops::FnMut(std::boxed::Box<std::ops::FnMut() + 'static>)`
Because Rust is strictly typed, the functions in the chain have to be of known types, and those types have to fit together.
That said, anything a dynamic language can do, Rust can emulate if you just implement enough of the dynamic typing machinery yourself. You can make a struct that contains a Vec<Box<FnMut(&Any) -> Any>> and an add_func generic method that takes some function and adds a wrapper that does the necessary unwrapping, checking and rewrapping to the Vec. The run method then calls these functions in order.
I have actually done this I think. This code may need some review, but I think everything can be implement in the following way:
Define the functions you wanna call:
fn f1() -> u64 {
println!("Hello world: 1");
2
}
fn f2(i: u64) -> Box<FnMut()> {
println!("Hello world: {}", i);
Box::new(|| println!("Hello world: {}", 3))
}
fn f3(mut f: Box<FnMut()>) {
f()
}
fn f4(_: Box<FnMut()>) -> bool {
println!("Hello world: 4");
true
}
Take the advantage of having Any type in Rust type system as same as closures:
use std::any::Any;
struct Bridge<'a> {
function: &'a mut FnMut(Box<Any>) -> Box<Any>,
}
The closures can then be used for type erasure of called functions. However, we would still need some work in function checking arguments:
fn run_chain(chain: &mut [Bridge]) -> Box<Any> {
if chain.is_empty() {
return Box::new(false)
}
let mut args;
{
let function = &mut chain.first_mut().unwrap().function;
args = function(Box::new(0));
}
for c in chain.iter_mut().skip(1) {
let res = (c.function)(args);
args = res;
}
args
}
fn main() {
let mut f1 = |_: Box<Any>| { let res = f1(); Box::new(res) as Box<Any> };
let mut f2 = |args: Box<Any>| { let res = f2(*args.downcast::<u64>().unwrap()); Box::new(res) as Box<Any> };
let mut f3 = |args: Box<Any>| { let res = f3(*args.downcast::<Box<FnMut()>>().unwrap()); Box::new(res) as Box<Any> };
let mut f4 = |args: Box<Any>| { let res = f4(*args.downcast::<Box<FnMut()>>().unwrap()); Box::new(res) as Box<Any> };
let mut fns: Vec<Bridge> = Vec::new();
fns.push(Bridge { function: &mut f1 });
fns.push(Bridge { function: &mut f2 });
fns.push(Bridge { function: &mut f3 });
let _ = run_chain(&mut fns);
fns.pop();
fns.push(Bridge { function: &mut f4 });
let res = run_chain(&mut fns);
println!("Result: {:?}", res.downcast::<bool>().unwrap());
}
So basically all we do is writing a closure wrapper with the same interface. The type checking can be done right inside the closure before passing the argument further and it can be checked so that it will not lead to a crash.

In Rust, how can I make this code less repetitive?

The goal is to write a function that gets two paths, input_dir and output_dir, and convertes all markdown files from input_dir to html files in output_dir.
I finally managed to get it to run but it was rather frustrating. The parts that should be hard are super easy: the actual conversion from Markdown to HTML is effectively only one line. The seemingly easy parts are what took me the longest. Using a vector of paths and put all files into it is something I replaced with the glob crate. Not because I couldn't get it to work but it was a mess of if let and unwrap. A simple function that iterates over the list of elements and figures out which of them are actually files and not directories? Either I need four indentation levels if if let or I freak out over matches.
What am I doing wrong?
But lets start with some things I tried to get a list of items in a directory filtered to only contain actual files:
use std::fs;
use std::vec::Vec;
fn list_files (path: &str) -> Result<Vec<&str>, &str> {
if let Ok(dir_list) = fs::read_dir(path) {
Ok(dir_list.filter_map(|e| {
match e {
Ok(entry) => match entry.file_type() {
Ok(_) => entry.file_name().to_str(),
_ => None
},
_ => None
}
}).collect())
} else {
Err("nope")
}
}
fn main() {
let files = list_files("testdir");
println!("{:?}", files.unwrap_or(Vec::new()));
}
So, this code doesn't build, because the file name in Line 10 doesn't live long enough. I guess I could somehow create an owned String but that would introduce another nesting level because OsStr.to_string() returns a Result.
Now I looked through the code of the glob crate and they just use a mutable vector:
fn list_files (path: &str) -> Result<Vec<&str>, &str> {
let mut list = Vec::new();
if let Ok(dir_list) = fs::read_dir(path) {
for entry in dir_list {
if let Ok(entry) = entry {
if let Ok(file_type) = entry.file_type() {
if file_type.is_file() {
if let Some(name) = entry.file_name().to_str() {
list.push(name)
}
}
}
}
}
Ok(list)
} else {
Err("nope")
}
}
This not only adds crazy nesting, it also fails with the same problem. If I change from Vec<&str> to Vec<String>, it works:
fn list_files (path: &str) -> Result<Vec<String>, &str> {
let mut list = Vec::new();
if let Ok(dir_list) = fs::read_dir(path) {
for entry in dir_list {
if let Ok(entry) = entry {
if let Ok(file_type) = entry.file_type() {
if file_type.is_file() {
if let Ok(name) = entry.file_name().into_string() {
list.push(name)
}
}
}
}
}
Ok(list)
} else {
Err("nope")
}
}
Looks like I should apply that to my first try, right?
fn list_files (path: &str) -> Result<Vec<String>, &str> {
if let Ok(dir_list) = fs::read_dir(path) {
Ok(dir_list.filter_map(|e| {
match e {
Ok(entry) => match entry.file_type() {
Ok(_) => Some(entry.file_name().into_string().ok()),
_ => None
},
_ => None
}
}).collect())
} else {
Err("nope")
}
}
At least a bit shorter… but it fails to compile because a collection of type std::vec::Vec<std::string::String> cannot be built from an iterator over elements of type std::option::Option<std::string::String>.
It is hard to stay patient here. Why does .filter_map return Options instead of just using them to filter? Now I have to change line 15 from }).collect()) to }).map(|e| e.unwrap()).collect()) which iterates once more over the result set.
That can't be right!
You can massively rely on ? operator:
use std::fs;
use std::io::{Error, ErrorKind};
fn list_files(path: &str) -> Result<Vec<String>, Error> {
let mut list = Vec::new();
for entry in fs::read_dir(path)? {
let entry = entry?;
if entry.file_type()?.is_file() {
list.push(entry.file_name().into_string().map_err(|_| {
Error::new(ErrorKind::InvalidData, "Cannot convert file name")
})?)
}
}
Ok(list)
}
Do not forget that you can split your code into functions or implement your own traits to simplify the final code:
use std::fs;
use std::io::{Error, ErrorKind};
trait CustomGetFileName {
fn get_file_name(self) -> Result<String, Error>;
}
impl CustomGetFileName for std::fs::DirEntry {
fn get_file_name(self) -> Result<String, Error> {
Ok(self.file_name().into_string().map_err(|_|
Error::new(ErrorKind::InvalidData, "Cannot convert file name")
)?)
}
}
fn list_files(path: &str) -> Result<Vec<String>, Error> {
let mut list = Vec::new();
for entry in fs::read_dir(path)? {
let entry = entry?;
if entry.file_type()?.is_file() {
list.push(entry.get_file_name()?)
}
}
Ok(list)
}
An alternative answer with iterators, playground
use std::fs;
use std::error::Error;
use std::path::PathBuf;
fn list_files(path: &str) -> Result<Vec<PathBuf>, Box<Error>> {
let x = fs::read_dir(path)?
.filter_map(|e| e.ok())
.filter(|e| e.metadata().is_ok())
.filter(|e| e.metadata().unwrap().is_file())
.map(|e| e.path())
.collect();
Ok(x)
}
fn main() {
let path = ".";
for res in list_files(path).unwrap() {
println!("{:#?}", res);
}
}

Resources