Currently, I change actix_web from version 3 to 4.0.0-rc.1 and for every response, it always add "Ok"/"Err" field as below
{
"Ok": [
"item1",
"item2"
]
}
It should return:
[
"item1",
"item2"
]
This is handler for the API
pub async fn get_data(db: web::Data<Pool>) -> HttpResponse {
let res = web::block(move || db_get_data(db)).await;
match res {
Ok(data_vec) => HttpResponse::Ok().json(data_vec),
Err(_) => HttpResponse::BadRequest().finish()
}
}
fn db_get_data(db: web::Data<Pool>) -> Result<Vec<String>, ()> {
let items = vec!["item1".to_string(), "item2".to_string()];
Ok(items)
}
How could I solve this issue?
There are two layers of Result: one from actix_web::web::block and another from db_get_data. Try something like this:
let res = web::block(move || db_get_data(db)).await;
match res {
Ok(Ok(data_vec)) => HttpResponse::Ok().json(data_vec),
_ => HttpResponse::BadRequest().finish(),
}
Related
I have this function:
fn init_clients() -> (upload_executor::Executor, download_executor::Executor) {
let client_fs = if USE_CLOUD {
None
} else {
Some(Arc::new(adapter_fs::Client::new(".")))
};
let client_cloud = if USE_CLOUD {
Some(Arc::new(
adapter_s3::Client::new(
"https://s3.region.aws.com",
"region",
"bucket_name",
"KEY_ID",
"KEY_SECRET",
)
.unwrap(),
))
} else {
None
};
let client = match USE_CLOUD {
true => client_cloud
.as_ref()
.map(|o| o.clone() as Arc<dyn AdapterTrait>)
.unwrap(),
false => client_fs
.as_ref()
.map(|o| o.clone() as Arc<dyn AdapterTrait>)
.unwrap(),
};
let upload_executor = upload_executor::Executor::new(client.clone());
let download_executor = download_executor::Executor::new(client);
(upload_executor, download_executor)
}
I would like to know how to make it more readable, like:
let upload_client: TraitA + TraitB = if USE_CLOUD {
upload_client = fs::new()
} else {
upload_client = s3::new()
}
Is it possible?
If you merged the sections together you could create something like this. Also keep in mind that I haven't checked this in an IDE so I am not completely sure if I correctly converted to an anonymous trait.
fn init_s3_client() -> Arc<adapter_s3::Client> {
let client = adapter_s3::Client::new(
"https://s3.region.aws.com",
"region",
"bucket_name",
"KEY_ID",
"KEY_SECRET");
match client {
Ok(x) => Arc::new(x),
Err(err) => panic!("Failed to create s3 client: {:?}", err),
}
}
fn init_clients() -> (upload_executor::Executor, download_executor::Executor) {
let client = match USE_CLOUD {
true => init_s3_client() as Arc<dyn AdapterTrait>,
false => Arc::new(adapter_fs::Client::new(".")) as Arc<dyn AdapterTrait>,
};
let upload_executor = upload_executor::Executor::new(client.clone());
let download_executor = download_executor::Executor::new(client);
(upload_executor, download_executor)
}
I'm struggling using list_append programmatically in Rust.
I have a table called Humidities:
{
"id": 177,
"Measurements": [
49
]
}
and I want to add elements. For example:
{
"id": 177,
"Measurements": [
49,
53
]
}
This is a working solution for python, which I've found here :
table = get_dynamodb_resource().Table("table_name")
result = table.update_item(
Key={
'hash_key': hash_key,
'range_key': range_key
},
UpdateExpression="SET some_attr = list_append(some_attr, :i)",
ExpressionAttributeValues={
':i': [some_value],
},
ReturnValues="UPDATED_NEW"
)
if result['ResponseMetadata']['HTTPStatusCode'] == 200 and 'Attributes' in result:
return result['Attributes']['some_attr']
Based on the python solution I've tried this:
#[tokio::main]
async fn main() -> Result<(), Error> {
let Opt { base } = Opt::from_args();
let shared_config = make_config(base).await?;
let client = Client::new(&shared_config);
client
.update_item()
.table_name("Humidities")
.key("id", AttributeValue::N(177.to_string()))
.update_expression("SET i = list_append(Measurements, :i)")
.expression_attribute_values("i", AttributeValue::N(53.to_string()))
.send()
.await?;
Ok(())
}
However, the result is:
Error: Unhandled(Error { code: Some("ValidationException"), message: Some("ExpressionAttributeValues contains invalid key: Syntax error; key: \"i\""), request_id: Some("05RSFGFJEEDPO7850LI2T91RGRVV4KQNSO5AEMVJF66Q9ASUAAJG"), extras: {} })
What am I doing wrong?
The examples only demonstrate how to add a single item: https://github.com/awslabs/aws-sdk-rust/blob/main/examples/dynamodb/src/bin/add-item.rs
I've also tried this:
#[tokio::main]
async fn main() -> Result<(), Error> {
let Opt { base } = Opt::from_args();
let shared_config = make_config(base).await?;
let client = Client::new(&shared_config);
client
.update_item()
.table_name("Humidities")
.key("id", AttributeValue::N(177.to_string()))
.update_expression("set #Measurements = list_append(#Measurements, :value)")
.expression_attribute_names("#Measurements", "Measurements")
.expression_attribute_values("value", AttributeValue::N(53.to_string()))
.return_values(aws_sdk_dynamodb::model::ReturnValue::AllNew)
.send()
.await?;
Ok(())
}
with a look at: Append a new object to a JSON Array in DynamoDB using NodeJS
Same result, value is unknown:
Error: Unhandled(Error { code: Some("ValidationException"), message: Some("ExpressionAttributeValues contains invalid key: Syntax error; key: \"value\""), request_id: Some("1A8VEOEVSB7LMAB47H12N7IKC7VV4KQNSO5AEMVJF66Q9ASUAAJG"), extras: {} })
I've found a solution. There have been two problems:
list_append expects two lists
expression_attribute_values expected :value instead of value
Running example:
async fn main() -> Result<(), Error> {
let Opt { base } = Opt::from_args();
let shared_config = make_config(base).await?;
let client = Client::new(&shared_config);
client
.update_item()
.table_name("Humidities")
.key("id", AttributeValue::N(177.to_string()))
.update_expression("set #Measurements = list_append(#Measurements, :value)")
.expression_attribute_names("#Measurements", "Measurements")
.expression_attribute_values(
":value", // changed from "value" to ":value"
AttributeValue::L(vec![AttributeValue::N(53.to_string())]), // use a vector of numbers instead of a number
)
.return_values(aws_sdk_dynamodb::model::ReturnValue::AllNew)
.send()
.await?;
Ok(())
}
I'm new to Rust and am trying to wrap my head around error handling.
I'm trying to return error if parsing the date goes wrong, here is the function:
pub fn create_posts(contents: &Vec<String>) -> Result<Vec<Post>, CreatePostError> {
const TITLE_SEP: &str = "Title: ";
const DESC_SEP: &str = "Description: ";
const DATE_SEP: &str = "Date: ";
const TAGS_SEP: &str = "Tags: ";
let mut posts: Vec<Post> = Vec::new();
for entry in contents {
let lines = entry.lines().collect::<Vec<_>>();
let metadata = lines[0].contains(&TITLE_SEP)
&& lines[1].contains(&DESC_SEP)
&& lines[2].contains(&DATE_SEP)
&& lines[3].contains(&TAGS_SEP);
if metadata {
let date = &lines[2][DATE_SEP.len()..];
let parsed_date = match NaiveDate::parse_from_str(date, "%Y-%m-%d") {
Ok(parsed_date) => parsed_date,
Err(e) => eprintln!("Error: {:?}", CreatePostError::ParseError { inner_err: e }),
};
let tags: Vec<String> = lines[3][TAGS_SEP.len()..]
.split(", ")
.map(|s| s.to_string())
.collect();
let mut article_content = String::new();
for line in &lines[4..] {
article_content.push_str(line);
article_content.push_str("\n")
}
let post = Post {
title: lines[0][TITLE_SEP.len()..].to_string(),
description: lines[1][DESC_SEP.len()..].to_string(),
date: parsed_date,
tags,
content: article_content,
};
posts.push(post);
} else {
return Err(CreatePostError::MetadataError);
}
}
return Ok(posts);
}
You can see the full code here since i wrote custom errors: link
The problem I'm having is with this part:
let date = &lines[2][DATE_SEP.len()..];
let parsed_date = match NaiveDate::parse_from_str(date, "%Y-%m-%d") {
Ok(parsed_date) => parsed_date,
Err(e) => eprintln!("Error: {:?}", CreatePostError::ParseError { inner_err: e }),
};
I'm getting match arms have incompatible types. Expected struct NaiveDate, found ()
Here is my enum and impl for the error:
#[derive(Debug)]
pub enum CreatePostError {
ReadFileError { path: PathBuf },
MetadataError,
ParseError { inner_err: ParseError },
}
impl fmt::Display for CreatePostError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::ReadFileError { path } => write!(f, "Error reading file {path:?}"),
Self::MetadataError => write!(f, "Some metadata is missing"),
Self::ParseError { inner_err } => {
write!(f, "Error parsing date: {inner_err}")
}
}
}
}
impl From<chrono::format::ParseError> for CreatePostError {
fn from(e: chrono::format::ParseError) -> Self {
CreatePostError::ParseError { inner_err: e }
}
}
You probably want to have a result here, here is a way to do it:
let parsed_date = match NaiveDate::parse_from_str(date, "%Y-%m-%d") {
Ok(parsed_date) => Ok(parsed_date),
Err(e) => {eprintln!("Error: {:?}", &e);
Err(CreatePostError::ParseError { inner_err: e }}),
}?;
The ? is saying: if the thing before is an error, return it, and if it is Ok, unwrap it.
This pattern is so common that rust's result gives a lot of utilities to make this kind of things easier. Here, the map_err function would make it more straightforward: map_err
see:
let parsed_date = NaiveDate::parse_from_str(date, "%Y-%m-%d")
.map_err(|e| {
eprintln!("Error: {:?}", &e);
CreatePostError::ParseError { inner_err: e }})?;
But it is only a matter of preference and it might be a lot to digest if you are just beginning, so you can choose the way that you like the most.
I am "serializing" and "deserializing" a generic struct to and from an SQLite database. The struct has two members whose values are of generic types, V and T, both constrained to the DataType trait. When I want to reconstruct these from the information in the database, I haven't been able to find a way around specifying match arms for every combination of V and T. Given that I will eventually have around 20 data types, that means 20 * 20 = 400 match arms. Is there any way around this? An unsafe solution is also acceptable.
Here is a MWE with two data types:
// A somewhat boilerplaty, but working solution to
// store and retrieve a generically typed struct
// in a SQLite database.
use rusqlite::{params, Connection, Statement};
use rusqlite::types::{ToSql, FromSql, ValueRef};
// This trait needs to be implemented for every type
// the GenericStruct will hold as a value.
trait DataType : ToSql + FromSql {
type TargetType;
fn convert(value: &ValueRef) -> Self::TargetType;
fn data_type(&self) -> &'static str;
}
impl DataType for String {
type TargetType = String;
fn data_type(&self) -> &'static str {
"String"
}
fn convert(value: &ValueRef) -> Self::TargetType {
String::from(value.as_str().unwrap())
}
}
impl DataType for usize {
type TargetType = usize;
fn data_type(&self) -> &'static str {
"usize"
}
fn convert(value: &ValueRef) -> Self::TargetType {
usize::try_from(value.as_i64().unwrap()).unwrap()
}
}
// This is the generic struct that is persisted in SQLite.
#[derive(Debug)]
struct GenericStruct<V: DataType, T: DataType> {
value: V,
time: T
}
// This is just to keep the database stuff together.
struct Database<'db> {
pub add_struct: Statement<'db>,
pub get_struct: Statement<'db>
}
impl<'db> Database<'db> {
pub fn new<'con>(connection: &'con Connection) -> Database<'con> {
// the table will hold both the value and its Rust type
connection.execute_batch("
create table if not exists GenericStruct (
value any not null,
value_type text not null,
time any not null,
time_type text not null
)
").unwrap();
Database {
add_struct: connection.prepare("
insert into GenericStruct
(value, value_type, time, time_type)
values
(?, ?, ?, ?)
").unwrap(),
get_struct: connection.prepare("
select
value, value_type, time, time_type
from GenericStruct
").unwrap()
}
}
}
pub fn main() {
let sqlite = Connection::open("generic.db").unwrap();
let mut database = Database::new(&sqlite);
let g1 = GenericStruct {
value: String::from("Hello World"),
time: 20090921
};
let g2 = GenericStruct {
value: 42,
time: String::from("now")
};
// Add the two structs to the sqlite database
database.add_struct.execute(
params![&g1.value, &g1.value.data_type(), &g1.time, &g1.time.data_type()]
).unwrap();
database.add_struct.execute(
params![&g2.value, &g2.value.data_type(), &g2.time, &g2.time.data_type()]
).unwrap();
// Now there are two different types in the database.
// Retrieve the two structs again.
let mut rows = database.get_struct.query([]).unwrap();
while let Some(row) = rows.next().unwrap() {
let data_type = row.get_unwrap::<_, String>(1);
let time_type = row.get_unwrap::<_, String>(3);
// I want to lookup the converter instead
// of explicitly listing alternatives...
match (data_type.as_str(), time_type.as_str()) {
("String", "usize") => {
println!("{:?}", GenericStruct {
value: String::convert(&row.get_ref_unwrap(0)),
time: usize::convert(&row.get_ref_unwrap(2))
});
},
("usize", "String") => {
println!("{:?}", GenericStruct {
value: usize::convert(&row.get_ref_unwrap(0)),
time: String::convert(&row.get_ref_unwrap(2))
});
},
_ => ()
}
}
}
I have also set it up in a playground here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7bb2403d88c3318348ba50d90763c225
You can do that with the following (pretty complex) macro.
macro_rules! generate_match {
// First, we generate a table of permutations.
// Suppose we have the tuple (String, usize, ()).
// The table we generate will be the following:
// [
// [ String, usize, () ]
// [ usize, (), String ]
// [ (), String, usize ]
// ]
// Empty case
{ #generate_permutations_table
$row:ident
match ($e:expr)
table = [ $($table:tt)* ]
rest = [ ]
transformed = [ $($transformed:ty,)* ]
} => {
generate_match! { #permutate_entry
$row
match ($e) { }
table = [ $($table)* ]
}
};
{ #generate_permutations_table
$row:ident
match ($e:expr)
table = [ $($table:tt)* ]
rest = [ $current:ty, $($rest:ty,)* ]
transformed = [ $($transformed:ty,)* ]
} => {
generate_match! { #generate_permutations_table
$row
match ($e)
table = [
$($table)*
[ $current, $($rest,)* $($transformed,)* ]
]
rest = [ $($rest,)* ]
transformed = [ $($transformed,)* $current, ]
}
};
// For each entry in the table, we generate all combinations of the first type with the others.
// For example, for the entry [ String, usize, () ] we'll generate the following permutations:
// [
// (String, usize)
// (String, ())
// ]
// Empty case
{ #permutate_entry
$row:ident
match ($e:expr) { $($match_tt:tt)* }
table = [ ]
} => {
match $e {
$($match_tt)*
_ => {}
}
};
{ #permutate_entry
$row:ident
match ($e:expr) { $($match_tt:tt)* }
table = [
[ $current:ty, $($others:ty,)* ]
$($table:tt)*
]
} => {
generate_match! { #generate_arm
$row
match ($e) { $($match_tt)* }
table = [ $($table)* ]
current = [ $current ]
others = [ $($others,)* ]
}
};
// Finally, We generate `match` arms from each pair.
// For example, for the pair (String, usize):
// ("String", "usize") => {
// let value = GenericStruct {
// value: <String as DataType>::convert(&row.get_ref_unwrap(0)),
// time: <usize as DataType>::convert(&row.get_ref_unwrap(2)),
// };
// // Process `value...`
// }
// Empty case: permutate the next table entry.
{ #generate_arm
$row:ident
match ($e:expr) { $($match_tt:tt)* }
table = [ $($table:tt)* ]
current = [ $current:ty ]
others = [ ]
} => {
generate_match! { #permutate_entry
$row
match ($e) { $($match_tt)* }
table = [ $($table)* ]
}
};
{ #generate_arm
$row:ident
match ($e:expr) { $($match_tt:tt)* }
table = [ $($table:tt)* ]
current = [ $current:ty ]
others = [ $first_other:ty, $($others:ty,)* ]
} => {
generate_match! { #generate_arm
$row
match ($e) {
$($match_tt)*
(stringify!($current), stringify!($first_other)) => {
let value = GenericStruct {
value: <$current as DataType>::convert(&$row.get_ref_unwrap(0)),
time: <$first_other as DataType>::convert(&$row.get_ref_unwrap(2)),
};
// Here you actually do whatever you want with the value. Adjust for your needs.
println!("{:?}", value);
}
}
table = [ $($table)* ]
current = [ $current ]
others = [ $($others,)* ]
}
};
// Entry
(
match ($e:expr) from ($($ty:ty),+) in $row:ident
) => {
generate_match! { #generate_permutations_table
$row
match ($e)
table = [ ]
rest = [ $($ty,)+ ]
transformed = [ ]
}
};
}
Invoke with:
generate_match!(
match ((data_type.as_str(), time_type.as_str()))
from (String, usize /* more types... */)
in row
);
Playground.
I'm using syn to parse Rust code. When I read a named field's type using field.ty, I get a syn::Type. When I print it using quote!{#ty}.to_string() I get "Option<String>".
How can I get just "String"? I want to use #ty in quote! to print "String" instead of "Option<String>".
I want to generate code like:
impl Foo {
pub set_bar(&mut self, v: String) {
self.bar = Some(v);
}
}
starting from
struct Foo {
bar: Option<String>
}
My attempt:
let ast: DeriveInput = parse_macro_input!(input as DeriveInput);
let data: Data = ast.data;
match data {
Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) => {
fields.named.iter().for_each(|field| {
let name = &field.ident.clone().unwrap();
let ty = &field.ty;
quote!{
impl Foo {
pub set_bar(&mut self, v: #ty) {
self.bar = Some(v);
}
}
};
});
}
_ => {}
},
_ => panic!("You can derive it only from struct"),
}
My updated version of the response from #Boiethios, tested and used in a public crate, with support of several syntaxes for Option:
Option
std::option::Option
::std::option::Option
core::option::Option
::core::option::Option
fn extract_type_from_option(ty: &syn::Type) -> Option<&syn::Type> {
use syn::{GenericArgument, Path, PathArguments, PathSegment};
fn extract_type_path(ty: &syn::Type) -> Option<&Path> {
match *ty {
syn::Type::Path(ref typepath) if typepath.qself.is_none() => Some(&typepath.path),
_ => None,
}
}
// TODO store (with lazy static) the vec of string
// TODO maybe optimization, reverse the order of segments
fn extract_option_segment(path: &Path) -> Option<&PathSegment> {
let idents_of_path = path
.segments
.iter()
.into_iter()
.fold(String::new(), |mut acc, v| {
acc.push_str(&v.ident.to_string());
acc.push('|');
acc
});
vec!["Option|", "std|option|Option|", "core|option|Option|"]
.into_iter()
.find(|s| &idents_of_path == *s)
.and_then(|_| path.segments.last())
}
extract_type_path(ty)
.and_then(|path| extract_option_segment(path))
.and_then(|path_seg| {
let type_params = &path_seg.arguments;
// It should have only on angle-bracketed param ("<String>"):
match *type_params {
PathArguments::AngleBracketed(ref params) => params.args.first(),
_ => None,
}
})
.and_then(|generic_arg| match *generic_arg {
GenericArgument::Type(ref ty) => Some(ty),
_ => None,
})
}
You should do something like this untested example:
use syn::{GenericArgument, PathArguments, Type};
fn extract_type_from_option(ty: &Type) -> Type {
fn path_is_option(path: &Path) -> bool {
leading_colon.is_none()
&& path.segments.len() == 1
&& path.segments.iter().next().unwrap().ident == "Option"
}
match ty {
Type::Path(typepath) if typepath.qself.is_none() && path_is_option(typepath.path) => {
// Get the first segment of the path (there is only one, in fact: "Option"):
let type_params = typepath.path.segments.iter().first().unwrap().arguments;
// It should have only on angle-bracketed param ("<String>"):
let generic_arg = match type_params {
PathArguments::AngleBracketed(params) => params.args.iter().first().unwrap(),
_ => panic!("TODO: error handling"),
};
// This argument must be a type:
match generic_arg {
GenericArgument::Type(ty) => ty,
_ => panic!("TODO: error handling"),
}
}
_ => panic!("TODO: error handling"),
}
}
There's not many things to explain, it just "unrolls" the diverse components of a type:
Type -> TypePath -> Path -> PathSegment -> PathArguments -> AngleBracketedGenericArguments -> GenericArgument -> Type.
If there is an easier way to do that, I would be happy to know it.
Note that since syn is a parser, it works with tokens. You cannot know for sure that this is an Option. The user could, for example, type std::option::Option, or write type MaybeString = std::option::Option<String>;. You cannot handle those arbitrary names.