Rust return leveldb Database instance - database

I want to use the leveldb database in Rust. Everything works fine if I have all the code in one function, but I want to split the code up and have different functions for creating an entry and reading from the db. The easiest method I thought of, was to return the database instance I created in create_database and then submit it as a parameter to the function. The Problem is that Rust doesn't allow me to use Database as a type.
This works:
use std::{env, fs};
use leveldb::database::Database;
use leveldb::iterator::Iterable;
use leveldb::kv::KV;
use leveldb::options::{Options, WriteOptions, ReadOptions};
pub fn create_database() {
let mut dir = env::current_dir().unwrap();
dir.push("demo");
let path_buf = dir.clone();
fs::create_dir_all(dir).unwrap();
let path = path_buf.as_path();
let mut options = Options::new();
options.create_if_missing = true;
// Create Database
let database = match Database::open(path, options) {
Ok(db) => {db},
Err(e) => {panic!("Failed to open database: {:?}", e)}
};
// Read from database
let read_opts = ReadOptions::new();
let res = database.get(read_opts, 1);
match res {
Ok(data) => {
assert!(data.is_some());
assert_eq!(data, Some(vec![1]));
}
Err(e) => {panic!("Failed to read from database: {:?}", e)}
};
let read_opts = ReadOptions::new();
let mut iter = database.iter(read_opts);
let entry = iter.next();
assert_eq!(
entry,
Some((1, vec![1]))
);
// Write to database
let write_ops = WriteOptions::new();
match database.put(write_ops, 1, &[1]) {
Ok(_) => {()},
Err(e) => {panic!("Failed to write to database: {:?}", e)}
};
}
But this doesn't:
use std::{env, fs};
use leveldb::database::Database;
use leveldb::iterator::Iterable;
use leveldb::kv::KV;
use leveldb::options::{Options, WriteOptions, ReadOptions};
pub fn create_database() -> Database {
let mut dir = env::current_dir().unwrap();
dir.push("demo");
let path_buf = dir.clone();
fs::create_dir_all(dir).unwrap();
let path = path_buf.as_path();
let mut options = Options::new();
options.create_if_missing = true;
// Create Database
let database = match Database::open(path, options) {
Ok(db) => {db},
Err(e) => {panic!("Failed to open database: {:?}", e)}
};
return database;
}
pub fn get(database: Database) {
// Read from database
let read_opts = ReadOptions::new();
let res = database.get(read_opts, 1);
match res {
Ok(data) => {
assert!(data.is_some());
assert_eq!(data, Some(vec![1]));
}
Err(e) => {panic!("Failed to read from database: {:?}", e)}
};
let read_opts = ReadOptions::new();
let mut iter = database.iter(read_opts);
let entry = iter.next();
assert_eq!(
entry,
Some((1, vec![1]))
);
}
pub fn put(database: Database) {
// Write to database
let write_ops = WriteOptions::new();
match database.put(write_ops, 1, &[1]) {
Ok(_) => {()},
Err(e) => {panic!("Failed to write to database: {:?}", e)}
};
}
If I execute this program I get this error:
error[E0107]: wrong number of type arguments: expected 1, found 0
--> src/db/db.rs:8:29
|
8 | pub fn create_database() -> Database {
| ^^^^^^^^ expected 1 type argument
error[E0107]: wrong number of type arguments: expected 1, found 0
--> src/db/db.rs:27:22
|
27 | pub fn get(database: Database) {
| ^^^^^^^^ expected 1 type argument
error[E0107]: wrong number of type arguments: expected 1, found 0
--> src/db/db.rs:48:22
|
48 | pub fn put(database: Database) {
| ^^^^^^^^ expected 1 type argument
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0107`.
error: could not compile `gcoin`
To learn more, run the command again with --verbose.
I have tried looking for examples to use this database but didn't find anything that matched. In the source code I found out that Database has a generic type, but couldn't find a way to fix my problem.

Solved! Thanks Locke and kmdreko

Related

Is it possible to "combine" similar types in rust match assignment? [duplicate]

This question already has an answer here:
Multiple return types in rust [duplicate]
(1 answer)
Closed 10 months ago.
I'm trying to implement a function in rust that iterates either on the StdIn Lines or a BuffReader Lines.
Both types have lines.next() method. After that, the code is just String manipulation that works.
I originally made it worked (with repetitive and implicitly-typed branches) but the two branches had similar code. This gave me the impression I can DRY/refactor my code better.
The problem is (I think) that my two match branches, in main, do not return exactly the same type. Is it possible to make them "stick together" because I'm only using one common method? (It reminds me of Python duck typing)
Here's my refacto try:
use clap::Parser;
use exitcode;
use std::any::Any;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Lines, Write};
fn colorist(lines: &mut dyn Any, mut writer: impl std::io::Write) {
while let Some(line_wrap) = lines.next() {
match line_wrap {
Ok(line) => {
// ...
let buffer = "Some value base on line";
match writeln!(writer, "{}", buffer){
Ok(()) => (),
Err(e) => {eprintln!("{:?}", e);}
};
},
Err(e) => {eprintln!("{:?}", e);}
}
}
}
fn main() {
let args = Args::parse(); // have one '--file' arg being a filepath or is a empty str ("")
let mut writer = io::stdout();
let mut lines: Lines<dyn Any> = match args.file.is_empty() { // either read from stdin or read the file
false => {
let file = File::open(args.file).unwrap();
let reader = BufReader::new(file);
reader.lines() // type `std::io::Lines<BufReader<File>>`
},
true => {
let stdin = io::stdin();
stdin.lock().lines() // type `std::io::Lines<StdinLock<'_>>`
}
};
colorist(&mut lines, &mut writer);
}
This is a toy project, and I'm still learning rust on my free time. I might have some bad designs there and here.
As #PitaJ & #Chayim-friedman suggested, Either solved my problem.
I ended up with:
use either::*;
fn colorist(lines: &mut Either<Lines<BufReader<File>>, Lines<StdinLock>>, mut writer: impl std::io::Write) {...}
fn main() {
let args = Args::parse();
let mut writer = io::stdout();
let stdin = io::stdin();
let mut lines = match args.file.is_empty(){
false => {
let file = File::open(args.file).unwrap();
let reader = BufReader::new(file);
either::Left(reader.lines())
},
true => {
either::Right(stdin.lock().lines())
}
};
colorist(&mut lines, &mut writer);
match write!(writer, "{}", RESET){
_ => (),
};
let _ = writer.flush();
std::process::exit(exitcode::OK);
}

What is the correct way to loop while error occurs

I am quite new to Rust, and I'm still trying to be sure I understand principles doing a small project creating a server that uses a TcpStream. The server asks for a client for a code, which has to be 8 characters long. If it's not, it shall ask again for a new code. Here is what I have so far :
fn loby_matcher(stream: &mut TcpStream) -> std::io::Result<(String, usize), >{
let mut game_code = String::new();
let mut reader = BufReader::new(stream);
let len : usize = reader.read_line(&mut game_code)?;
match len {
8 => len,
_ => return Err(Error::new(ErrorKind::Other, "wrong game code")),
};
Ok((game_code, len))
}
pub fn create_game_matcher(mut stream: TcpStream) -> std::io::Result<()>{
println!("Creating a new game! Waiting for the code.");
let game_code: String;
let len: usize;
loop {
(game_code, len) = match loby_matcher(&mut stream) {
Ok(game_code) => break (game_code, len),
Err(e) => {
stream.write("Wrong code\n".as_bytes());
(String::new(),0)
}
};
};
println!("Received : {} size {}", game_code, len);
// println!("Closing connection now I guess?");
Ok(())
}
fn start_server(address: &str, port: &str) -> std::io::Result<()>{
let listener = TcpListener::bind(format!("{}:{}", address, port))?;
for stream in listener.incoming() {
// not using ? as we do not want to stop the server for wrong connection
let stream = stream.unwrap();
thread::spawn(move || {
create_game_matcher(stream);
});
}
Ok(())
}
The loop asking for a new code when an error occurs seems wrong, but I cannot figure out a way of doing that.
Also, I know I should create a custom error in order to be able to make a difference between a wrong code and an I/O error on my TCPstream, which I will do later.
What would be the right way of looping while a specific error occurs?
Edit
If you see anything wrong in the code, do not hesitate to tell me how I could improve it.
I'm assuming you're asking how to resolve the issue in create_game_matcher. If so, then you need to move your assignment further out.
To compare the error inside io::Error, can be cumbersome. You can check the kind using e.kind() == ErrorKind::Other. However, the actual error could be anything, and doesn't require implementing PartialEq for &str. The easiest is probably just to do e.to_string() == "wrong game code".
But like you mention, the most idiomatic approach would be to create a your own error type.
pub fn create_game_matcher(mut stream: TcpStream) -> std::io::Result<()> {
println!("Creating a new game! Waiting for the code.");
let (game_code, len) = loop {
match loby_matcher(&mut stream) {
Ok(game_code) => break game_code,
// Err(e) if e.kind() == ErrorKind::Other && e.to_string() == "wrong game code" => {
// or just (for ease of use)
Err(e) if e.to_string() == "wrong game code" => {
stream.write("Wrong code\n".as_bytes())?;
}
Err(e) => return Err(e),
};
};
println!("Received : {} size {}", game_code, len);
// println!("Closing connection now I guess?");
Ok(())
}
Since you want to loop until loby_matcher succeeds, then (String::new(), 0) can be removed. You also forgot to check whether stream.write failed, so I've added an ?.
Alternatively, given that you always break after the assignment, you could also express it like this:
pub fn create_game_matcher(mut stream: TcpStream) -> std::io::Result<()> {
println!("Creating a new game! Waiting for the code.");
let game_code: String;
let len: usize;
loop {
match loby_matcher(&mut stream) {
Ok((game_code_, len_)) => {
game_code = game_code_;
len = len_;
break;
}
Err(e) if e.to_string() == "wrong game code" => {
stream.write("Wrong code\n".as_bytes())?;
}
Err(e) => return Err(e),
};
}
println!("Received : {} size {}", game_code, len);
// println!("Closing connection now I guess?");
Ok(())
}

Multiple borrow in previous iteration of loop

Following is the code I'm working on, I explore a directory path with a queue and I want to store the filesystem tree in my data structure (the enum Entry):
use failure::Error;
use std::collections::VecDeque;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
fn main() -> Result<(), Error> {
let paths = visit_dir(Path::new(".")).map_err(Error::from)?;
Ok(())
}
#[derive(Debug)]
enum Entry {
Dir(PathBuf, Vec<Entry>),
File(PathBuf),
}
impl Entry {
fn new_dir(path: &Path) -> Entry {
Entry::Dir(path.to_path_buf(), Vec::new())
}
fn new_file(path: &Path) -> Entry {
Entry::File(path.to_path_buf())
}
/// Append a new Entry to self if self is a directory.
fn push(&mut self, path: &Path) -> Option<&mut Entry> {
if let Entry::Dir(_, ref mut content) = self {
let entry = if path.is_dir() {
Entry::new_dir(path)
} else {
Entry::new_file(path)
};
content.push(entry);
return content.last_mut();
}
None
}
fn path(&self) -> &Path {
match self {
Entry::Dir(path, _) => path,
Entry::File(path) => path,
}
}
}
fn visit_dir(root: &Path) -> io::Result<Entry> {
let mut dir = Entry::new_dir(root);
let mut queue = VecDeque::new();
queue.push_back(&mut dir);
while !queue.is_empty() {
let parent = queue.pop_front().unwrap();
let path = parent.path();
if path.is_dir() {
for entry in fs::read_dir(path)? {
let entry = entry?;
let path = entry.path();
let entry = parent.push(&path).unwrap();
if path.is_dir() {
queue.push_back(entry);
}
}
}
}
Ok(dir)
}
Link to Playground
The error I am getting is:
error[E0499]: cannot borrow `*parent` as mutable more than once at a time
--> src/main.rs:61:29
|
61 | let entry = parent.push(&path).unwrap();
| ^^^^^^ mutable borrow starts here in previous iteration of loop
My questions are:
Why exactly do I get this error?
Are there any workaround to solve it?
entry borrows parent mutably in the line:
let entry = parent.push(&path).unwrap();
Then, you do not release entry, because you store it in queue. So, parent is still borrowed mutably when this is the 2nd iteration in the loop. This is unsafe to do that.
What you are trying to do is a tree of mutable references. That won't work in Rust, and this is generally a bad idea. You should modify the way you want to implement that.

How to save a file downloaded from S3 with Rusoto to my hard drive?

I am trying to download a file from a bucket with Rusoto and I am getting the file content:
fn get_object(client: &TestClient, bucket: &str, filename: &str) {
let get_req = GetObjectRequest {
bucket: bucket.to_owned(),
key: filename.to_owned(),
..Default::default()
};
let result = client.get_object(&get_req).sync().expect("Couldn't GET object");
let stream = result.body.unwrap();
let body = stream.concat2().wait().unwrap();
assert!(body.len() > 0);
}
How can I save this GetObjectOutput(result) object to a file?
Rusoto now uses the standard library futures and no longer offers the sync method, so the previous answer is no longer valid.
Reading to memory
use futures::stream::TryStreamExt;
use rusoto_core::Region;
use rusoto_s3::{GetObjectRequest, S3Client, S3};
type Error = Box<dyn std::error::Error>;
type Result<T, E = Error> = std::result::Result<T, E>;
const BUCKET_NAME: &str = "my very own bucket name";
#[tokio::main]
async fn main() -> Result<()> {
let client = S3Client::new(Region::UsEast2);
let mut object = client
.get_object(GetObjectRequest {
bucket: BUCKET_NAME.into(),
..Default::default()
})
.await?;
let body = object.body.take().expect("The object has no body");
let body = body.map_ok(|b| b.to_vec()).try_concat().await?;
println!("body length: {}", body.len());
Ok(())
}
AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY need to be specified. I chose to set environment variables outside of the code.
Streaming to a file
use rusoto_core::Region;
use rusoto_s3::{GetObjectRequest, S3Client, S3};
use tokio::{fs::File, io};
type Error = Box<dyn std::error::Error>;
type Result<T, E = Error> = std::result::Result<T, E>;
const BUCKET_NAME: &str = "my very own bucket name";
#[tokio::main]
async fn main() -> Result<()> {
let client = S3Client::new(Region::UsEast2);
let mut object = client
.get_object(GetObjectRequest {
bucket: BUCKET_NAME.into(),
..Default::default()
})
.await?;
let body = object.body.take().expect("The object has no body");
let mut body = body.into_async_read();
let mut file = File::create("/tmp/a-place-to-write").await?;
io::copy(&mut body, &mut file).await?;
Ok(())
}
While ByteStream has an alluring into_blocking_read method, I do not recommend using it. If you attempt to use it inside of an async context, you get a panic because it starts a nested Tokio executor. If you use it outside of an async context, it will truncate the data unless you take great care to have the async runtime around but not to be within it.
See also:
Writing a chunk stream to a file asynchronously using hyper
How do I write a futures::Stream to disk without storing it entirely in memory first?
Dependency versions
[dependencies]
rusoto_s3 = "0.43.0"
rusoto_core = "0.43.0"
tokio = { version = "0.2.21", features = ["macros"] }
futures = "0.3.5"
You're almost there. Your code will put the object in body, which is a Vec<u8>.
To write the contents of body to a file:
use std::io::Write;
use std::fs::File;
let mut file = File::create("/path/to/my-object").expect("create failed");
file.write_all(&body).expect("failed to write body");

Parsing text file lines into numbers using std::iter::Iterator map

I'm trying to read and parse a text file in Rust. Each line is a signed integer. I'm able to do it using for line in lines iteration but I'm unable to do it with a iter().map(|l| ...) one-liner. I'm getting a
expected `&core::result::Result<collections::string::String, std::io::error::Error>`,
found `core::result::Result<_, _>`
when I try to pattern match Ok(s) => match s.parse() but I'm unable to get to the bottom of what I am doing wrong. The whole example is below. The code on the bottom is the code that is producing the error.
Can anyone tell what I am doing wrong?
use std::error::Error;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
use std::path::Path;
fn main() {
// Create a path to the desired file
let path = Path::new("input/numbers.txt");
let display = path.display();
// Open the path in read-only mode, returns `io::Result<File>`
let file = match File::open(&path) {
// The `description` method of `io::Error` returns a string that describes the error
Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
Ok(file) => file,
};
// Collect all lines into a vector
let reader = BufReader::new(file);
let lines: Vec<_> = reader.lines().collect();
// Works.
let mut nums = vec![];
for l in lines {
println!("{:?}", l);
let num = match l {
Ok(s) => match s.parse() {
Ok(i) => i,
Err(_) => 0
},
Err(_) => 0
};
nums.push(num);
}
// Doesn't work!
let nums: Vec<i64> = lines.iter().map(|l| match l {
Ok(s) => match s.parse() {
Ok(i) => i,
Err(_) => 0
},
Err(_) => 0
});
}
Let's look at the complete error message, which points to the error for us:
<anon>:5:9: 5:14 error: mismatched types:
expected `&core::result::Result<&str, ()>`,
found `core::result::Result<_, _>`
(expected &-ptr,
found enum `core::result::Result`) [E0308]
<anon>:5 Ok(s) => match s.parse() {
^~~~~
The compiler is expecting a &Result, but found a Result, and the issue is with the Ok(s) pattern. The type of l is a reference to a Result because you are using iter - which returns an iterator of references to the items in the vector.
The shortest fix is to add a & to the pattern match for the closure variable:
fn main() {
let lines: Vec<Result<_, ()>> = vec![Ok("1"), Ok("3"), Ok("5")];
// HERE V
let nums: Vec<i64> = lines.iter().map(|&l| match l {
Ok(s) => match s.parse() {
Ok(i) => i,
Err(_) => 0
},
Err(_) => 0
}).collect();
println!("{:?}", nums)
}
I also had to add collect to go back to a Vec.
The other change you could make would be to consume the input vector using into_iter and then iterate on each value in the vector:
// HERE V~~~~
let nums: Vec<i64> = lines.into_iter().map(|l| match l {
And for good measure, you could use ok, and_then, and unwrap_or to say the same thing a bit more succinctly:
let nums: Vec<i64> = lines.into_iter().map(|l| {
l.ok().and_then(|s| s.parse().ok()).unwrap_or(0)
}).collect();

Resources