-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Automatically parse strings into structs that implement FromStr #111
Comments
Awesome!!! I should close next release and I'll work on it. |
Ok, I took a look to this and I need to write some questions and notes.
I come back later with a sketch of how to perform conversion. |
Ok, I found a way to implement it based on https://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html In our case what change is the receiver, but use This approach is almost optimal because give also a good error description if the receiver type doesn't implement
use std::str::FromStr;
use std::{fmt::Debug, marker::PhantomData};
struct Wrap<T>(std::marker::PhantomData<T>);
trait ViaParseDebug<'a, T> {
fn magic_conversion(&self, input: &'a str) -> T;
}
impl<'a, T> ViaParseDebug<'a, T> for &&Wrap<T>
where
T: FromStr,
T::Err: Debug,
{
fn magic_conversion(&self, input: &'a str) -> T {
T::from_str(input).unwrap()
}
}
trait ViaParse<'a, T> {
fn magic_conversion(&self, input: &'a str) -> T;
}
impl<'a, T> ViaParse<'a, T> for &Wrap<T>
where
T: FromStr,
{
fn magic_conversion(&self, input: &'a str) -> T {
match T::from_str(input) {
Ok(v) => v,
Err(_) => {
panic!("Cannot parse '{}' to get T", input);
}
}
}
}
trait ViaIdent<'a, T> {
fn magic_conversion(&self, input: &'a str) -> T;
}
impl<'a> ViaIdent<'a, &'a str> for &&Wrap<&'a str> {
fn magic_conversion(&self, input: &'a str) -> &'a str {
input
}
}
#[derive(Debug)]
struct S;
impl FromStr for S {
type Err = ();
fn from_str(_s: &str) -> Result<Self, Self::Err> {
Ok(S)
}
}
#[derive(Debug)]
struct ErrorNotImplementDebug;
struct ErrorWithoutDebug;
impl FromStr for ErrorNotImplementDebug {
type Err = ErrorWithoutDebug;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"error" => Err(ErrorWithoutDebug),
_ => Ok(ErrorNotImplementDebug),
}
}
}
#[allow(dead_code)]
struct NotImplementFromStr;
type StrTypeAlias = &'static str;
fn main() {
let u = (&&&Wrap::<u32>(PhantomData)).magic_conversion("42");
println!("{}", u);
let s = (&&&Wrap::<&str>(PhantomData)).magic_conversion("hi");
println!("{}", s);
let str_type_alias = (&&&Wrap::<StrTypeAlias>(PhantomData)).magic_conversion("alias");
println!("{}", str_type_alias);
let parse_without_error =
(&&&Wrap::<ErrorNotImplementDebug>(PhantomData)).magic_conversion("42");
println!("{:?}", parse_without_error);
let _parse_without_error =
(&&&Wrap::<ErrorNotImplementDebug>(PhantomData)).magic_conversion("error");
// let not_implement: NotImplementFromStr = (&&&Wrap::<NotImplementFromStr>(PhantomData)).magic_conversion("42");
} |
Test list:
But cannot work for fixture arguments because we need to know the receiver type in order to use the trick. A |
We should recover follow unit tests in the new implementation mod arg_2_fixture_should {
use super::{assert_eq, *};
mod call_fixture {
use super::{assert_eq, *};
fn test(function: &str, expected: &str) {
let ast = function.ast();
let arg = first_arg_ident(&ast);
let line = arg_2_fixture(arg, &EmptyResolver);
assert_eq!(line, expected.ast());
}
#[test]
fn as_is() {
test("fn foo(fix: String) {}", "let fix = fix::default();");
}
#[test]
fn by_remove_underscore_if_any() {
test("fn foo(_fix: String) {}", "let _fix = fix::default();");
}
#[test]
fn do_not_remove_more_underscores() {
test("fn foo(f_i_x: String) {}", "let f_i_x = f_i_x::default();");
}
#[test]
fn do_not_remove_two_underscores() {
test("fn foo(__fix: String) {}", "let __fix = __fix::default();");
}
#[test]
fn without_add_mut() {
test("fn foo(mut fix: String) {}", "let fix = fix::default();");
}
}
mod call_given_fixture {
use super::{assert_eq, *};
fn test(function: &str, fixture: &str, call: Expr, expected: &str) {
let ast = function.ast();
let arg = first_arg_ident(&ast);
let mut resolver = HashMap::new();
resolver.insert(fixture.to_owned(), &call);
let line = arg_2_fixture(arg, &resolver);
assert_eq!(line, expected.ast());
}
#[test]
fn as_is() {
test(
"fn foo(mut fix: String) {}",
"fix",
expr("bar()"),
"let fix = bar();",
);
}
#[test]
fn by_remove_underscore() {
test(
"fn foo(_fix: String) {}",
"fix",
expr("bar()"),
"let _fix = bar();",
);
}
}
} |
Next steps:
|
Completed |
In parametrized tests involving structs that implement
FromStr
it would be cool if strings given in acase
could automatically be parsed into the corresponding type (similar to the way arguments are magically converted in structopt).For example, one of my tests looks something like this:
What I would like to be able to do is the following:
The text was updated successfully, but these errors were encountered: