I am using nickel.rs, PostgreSQL, and Angular.js. I can insert into my table with an HTTP POST:
// insert
{
let conn = shared_connection.clone();
router.post("/api/movies", middleware! { |request, mut response|
let conn = conn.lock().unwrap();
let stmt = match conn.prepare("insert into movie (title, releaseYear, director, genre)
values ($1, $2, $3, $4)") {
Ok(stmt) => stmt,
Err(e) => {
return response.send(format!("Preparing query failed: {}", e));
}
};
let movie = request.json_as::<MovieInsert>().unwrap();
match stmt.execute(&[
&movie.title.to_string(),
&movie.releaseYear,
&movie.director.to_string(),
&movie.genre.to_string()
]) {
Ok(v) => println!("Inserting movie was Success."),
Err(e) => println!("Inserting movie failed. => {:?}", e),
};
// ERROR (1)
// return response.set(Location("/".into()));
});
}
I know this works fine because the row is inserted in the PostgreSQL table. However, the Chrome web browser shows an error:
POST http://localhost:6767/api/movies 404 (Not Found)
I also added the code in ERROR (1) line
response.set(Location("/".into()));
however, console show the error.
expected `core::result::Result<nickel::middleware::Action<nickel::response::Response<'mw, _>, nickel::response::Response<'mw, _, hyper::net::Streaming>>, nickel::nickel_error::NickelError<'mw, _>>`,
found `&mut nickel::response::Response<'_, _>`
(expected enum `core::result::Result`,
found &-ptr)
Now it is my code applied what Shepmaster said.
// insert
{
let conn = shared_connection.clone();
router.post("/api/movies", middleware! { |request, mut response|
let conn = conn.lock().unwrap();
let stmt = match conn.prepare("insert into movie (title, releaseYear, director, genre)
values ($1, $2, $3, $4)") {
Ok(stmt) => stmt,
Err(e) => {
return response.send(format!("Preparing query failed: {}", e));
}
};
let movie = request.json_as::<MovieInsert>().unwrap();
match stmt.execute(&[
&movie.title.to_string(),
&movie.releaseYear,
&movie.director.to_string(),
&movie.genre.to_string()
]) {
Ok(v) => println!("Inserting movie was Success."),
Err(e) => println!("Inserting movie failed. => {:?}", e),
};
response.set(StatusCode::PermanentRedirect)
.set(Location("/".into()));
""
});
}
but the error occurred.
src/main.rs:155:18: 155:43 error: the trait modifier::Modifier<nickel::response::Response<'_, _>> is not implemented for the type hyper::header::common::location::Location [E0277]
src/main.rs:155 .set(Location("/".into()));
finally I fix like this!
Ok(v) => {
println!("Inserting movie was Success.");
response.set(StatusCode::Ok);
},
Err(e) => println!("Inserting movie failed. => {:?}", e),
};
//response.set(StatusCode::PermanentRedirect)
// .set(Location("/".into()));
//""
return response.send("");
The code, as currently listed, doesn't make any sense. There's nothing returned from your route handler:
match ... {
Ok(v) => println!("Inserting movie was Success."),
Err(e) => println!("Inserting movie failed. => {:?}", e),
};
Because nothing about the database or the frontend framework is important, your code is equivalent to this:
#[macro_use]
extern crate nickel;
use nickel::{Nickel, HttpRouter};
fn main() {
let mut server = Nickel::new();
server.post("/api/movies", middleware! { |request, mut response|
println!("Hello");
});
server.listen("127.0.0.1:6767");
}
If you return something from the handler, then the HTTP status code changes from a 404 to a 200.
If you'd like to redirect somewhere, you need to explicitly change the status code. The documentation for Response::set happens to have an example:
server.get("/a", middleware! { |_, mut res|
res.set(StatusCode::PermanentRedirect)
.set(Location("http://nickel.rs".into()));
""
});
Note that an empty string is returned, similar to before. You were trying to return the Response type directly.
Related
I'm trying to print out the error it throws when bmp::open failed
let image_result = bmp::open(argument);
let image = match image_result {
Ok(i) => i,
Err(error) => {
println!("Error! {error:?}");
},
};
However, I've been getting the below error message
Thanks in advance!
This is because in Rust, the match statement has to return the same type from its match arms. So your first match arm
Ok(i) => i
returns a type of Image where as second match arm
Err(error) => {
println!("Error! {error:?}");
}
does not return anything hence the compiler infer the return type as ()(unit) type.
There are multiple ways you could solve this, but it's really depends upon how you want to handle with the error case. If your intention is to handle only Ok case, you could destructure the Result.
if let Ok(i) = bmp::open(argument) {
print("Do something with {i}")
}
Alternatively you can panic if the file is failed to open.
let image_result = bmp::open(argument);
let image = match image_result {
Ok(i) => i,
Err(error) => {
panic!("Error! {error:?}");
}
};
OR with unwrap_or_else,
let img = bmp::open("test/rgbw.bmp").unwrap_or_else(|e| {
panic!("Failed to open: {}", e);
});
I wrote this code and it works. But I have a custom class for outputting logs to stdout. I want to call my logging function if I get an Error instead of panic! macro.
fn create_logfile() -> File {
let timestamp = Log::format_date(chrono::Local::now());
let filename = format!("{}.log", timestamp);
let logfile = match File::create(filename) {
Ok(file) => file,
Err(error) => {
panic!("There was a problem creating the file: {:?}", error)
}
};
logfile
}
For example I want get something like that:
let logfile = match File::create(filename) {
Ok(file) => file,
Err(e) => {
Log::error("Log file creation failed, reason: {}", e);
process::exit(1)
}
};
But compiler says:
[E0308] `match` arms have incompatible types.
[Note] expected struct `File`, found `()
How can I solve this problem?
If I put the error data to stderr will it help?
Your revised example with std::process::exit() works for me: (link)
use chrono; // 0.4.23
use std::fs::File;
use log; // 0.4.17
struct Log { }
impl Log {
fn format_date(date: chrono::DateTime<chrono::offset::Local>) -> i64 {
return 0;
}
}
fn old_create_logfile() -> File {
let timestamp = Log::format_date(chrono::Local::now());
let filename = format!("{}.log", timestamp);
let logfile = match File::create(filename) {
Ok(file) => file,
Err(error) => {
panic!("There was a problem creating the file: {:?}", error)
}
};
logfile
}
fn new_create_logfile() -> File {
let timestamp = Log::format_date(chrono::Local::now());
let filename = format!("{}.log", timestamp);
let logfile = match File::create(filename) {
Ok(file) => file,
Err(e) => {
// Instead of using `Log::error`, we'll use `log::error!` for show.
log::error!("Log file creation failed, reason: {}", e);
// This compiles.
std::process::exit(1)
}
};
logfile
}
fn main() {
new_create_logfile();
}
Normally, you need to make sure the return types of all match arms have the same type -- here, you are returning std::fs::File under the Ok branch, so the Err branch can "escape" the requirement by returning ! (pronounced "never") (link).
Since computation never returns from the std::process::exit() and its return type is marked as !, it's passes the type-checking stage.
I am working on my first Rust project, a CLI application to find large files on a local filesystem. Rust has excellent documentation regarding match statements but I do not see how I can assign a value returned by the function passed to the match statement in a new variable:
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
let mut results_map: Option<Results>;
match search(&config.root_path) {
Err(e) => eprintln!("Error when calling run(): {:?}", e),
Ok(results) => results_map = results), // how to properly assign value returned by function here?
}
Ok(())
}
pub struct Results {
result_map: HashMap<String, u64>,
}
pub fn search(path: &str) -> Result<Results, io::Error> {
let root_path = Path::new(path);
let mut results = Results { result_map: HashMap::<String, u64>::new()};
match visit_dirs(root_path, &mut results) {
Err(e) => eprintln!("Error calling visit_dirs() from search(): {:?}", e),
_ => (),
}
Ok(results)
}
The functions you're calling in the match statements, search and visit_dirs return Result types. You have to use one of the arms of the match statement to cover the Ok (happy) case, next to where you're already covering the Err (error) case:
match search(&config.root_path) {
Ok(results) => return results,
Err(e) => eprintln!("Error when calling run(): {:?}", e),
}
Or you can assign the result of the match expression to a new variable:
let new_variable = match search(&config.root_path) {
Ok(results) => results, // no return here
Err(e) => {
eprintln!("Error when calling run(): {:?}", e)
// Either choose to panic (not recommended)
// panic!("Bad times lie ahead")
// Or propagate the error from your own Result
return Err(e);
},
}
You may want to look into the ? operator to simplify this in the common case.
I'm attempting to open and write to a database in a Rust library I will call from python, with the help of pyo3. If an error occurs, I would like to raise an exception that can be caught in the calling Python process, but I'm having difficulties terminating execution and raising an error.
use rusqlite::{Connection};
use rusqlite::NO_PARAMS;
use pyo3::{Python, wrap_pyfunction};
use pyo3::exceptions::PyIOError;
#[pyfunction]
fn do_something(_py: Python) -> PyResult<u32> {
match Connection::open("database.sql") {
Ok(t) => conn = t,
Err(e) => {
let gil = Python::acquire_gil();
let py = gil.python();
let error_message = format!("Unable to open database! {}", e.to_string());
PyIOError::new_err(error_message).restore(py)
}
};
match conn.execute(
"create table if not exists cats (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
)",
NO_PARAMS,
) {
Ok(_t) => (),
Err(e) => {
let gil = Python::acquire_gil();
let py = gil.python();
let error_message = format!("Unable to open database! {}", e.to_string());
PyIOError::new_err(error_message).restore(py)
}
}
Ok(0)
It's my understanding that by calling the restore function on the PyIOError object, an error would be raised, however, I must be misunderstanding because the compiler seems to consider it a possibility that conn is not initialised:
error[E0381]: borrow of possibly-uninitialized variable: `conn`
18 | match conn.execute(
| ^^^^ use of possibly-uninitialized `conn`
What would be an appropriate approach here?
First of all, your Ok(t) = conn = t fails, as you haven't defined conn. So prior to the match add let conn;. Alternatively, you can also just assign the result of the match to conn.
Second, you still need to return an Err.
#[pyfunction]
fn do_something(_py: Python) -> PyResult<u32> {
let conn = match Connection::open("database.sql") {
Ok(t) => t,
Err(e) => {
let gil = Python::acquire_gil();
let py = gil.python();
let error_message = format!("Unable to open database! {}", e.to_string());
PyIOError::new_err(error_message).restore(py);
return Err(PyErr::fetch(py));
}
};
match conn.execute(
"create table if not exists cats (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
)",
NO_PARAMS,
) {
Ok(_t) => (),
Err(e) => {
let gil = Python::acquire_gil();
let py = gil.python();
let error_message = format!("Unable to open database! {}", e.to_string());
PyIOError::new_err(error_message).restore(py);
return Err(PyErr::fetch(py));
}
}
Ok(0)
}
It's been some time since I used PyO3. But unless I remember incorrectly, then you can just remove restore() also just return the Err and let PyO3 handle the rest.
#[pyfunction]
fn do_something(_py: Python) -> PyResult<u32> {
let conn = match Connection::open("database.sql") {
Ok(t) => t,
Err(e) => {
let error_message = format!("Unable to open database! {}", e.to_string());
return Err(PyIOError::new_err(error_message));
}
};
match conn.execute(
"create table if not exists cats (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
)",
NO_PARAMS,
) {
Ok(_t) => (),
Err(e) => {
let error_message = format!("Unable to open database! {}", e.to_string());
return Err(PyIOError::new_err(error_message));
}
}
Ok(0)
}
So I have the following code that sends multithreaded requests to a list of domains. So far I am able to grab individual data from the response, such as the bytes, or the url, or the status code, etc. but not all of them together. I would like to store all of these values into vectors so it can be written to a file. I know this is probably a super dumb question, but i've been working on it for days and can't figure it out. Any help is appreciated!
#[tokio::main]
async fn get_request(urls: Vec<&str>, paths: Vec<&str>) {
let client = Client::new();
for path in paths {
let urls = urls.clone();
let bodies = stream::iter(urls)
.map(|url| {
let client = &client;
async move {
let mut full_url = String::new();
full_url.push_str(url);
full_url.push_str(path);
let resp = client
.get(&full_url)
.timeout(Duration::from_secs(3))
.send()
.await;
resp
}
})
.buffer_unordered(PARALLEL_REQUESTS);
bodies
.for_each(|b| async {
match b {
Ok(b) => {
let mut body_test = &b.bytes().await;
match body_test {
Ok(body_test) => {
let mut stringed_bytes = str::from_utf8(&body_test);
match stringed_bytes {
Ok(stringed_bytes) => {
println!("stringed bytes: {}", stringed_bytes);
}
Err(e) => println!("Stringified Error: {}", e),
}
}
Err(e) => println!("body Error: {}", e),
}
}
Err(e) => println!("Got an error: {}", e),
}
})
.await;
}
}
I would try creating a struct that would store your desired outputs that you would like to acquire from the body or the hyper::Response. For example:
struct DesiredContent {
code: Option<u16>,
body: Json
...
}
And then implementing impl TryFrom<hyper::Response> for DesiredContent
(This assumes you might have cases that should fail like - 404 errors)