-
Notifications
You must be signed in to change notification settings - Fork 224
Foundry UI #3392
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
base: master
Are you sure you want to change the base?
Foundry UI #3392
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good I think, left some comments about possible improvements
#[must_use] | ||
pub fn from_flag(json: bool) -> Self { | ||
if json { | ||
OutputFormat::Json | ||
} else { | ||
OutputFormat::Human | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd just allow creating it by passing an enum and force the caller to deal with that. from_flag
seems bit weird.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this enum was previously in print.rs
, I've just moved it. from_flag
corresponds to --json
flag so I think it actually makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd still prefer we remove that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any specific reason? I don't really see a point to remove it - it's the simplest way to create it based on bool:
starknet-foundry/crates/sncast/src/main.rs
Line 217 in 32302f6
let output_format = OutputFormat::from_flag(cli.json); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's unnecessary indirection for something this simple. The usage you posted is obvious but from other places, it's not obvious at all what from_flag
method does since arguments name aren't easily visible to the caller.
Why not
let output_format = if cli.json {
OutputFormat::Json
} else {
OutputFormat::Human
};
at the caller site? We aren't saving any code from doing this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not at the caller site?
Imo it's not a good practice to write ifs logic directly at the level of main
function, even if they are simple.
Let's follow your reasoning and assume we introduce other simple flags in future.
fn main() -> Result<()> {
let cli = Cli::parse();
let numbers_format = NumbersFormat::from_flags(cli.hex_format, cli.int_format);
let output_format = if cli.json {
OutputFormat::Json
} else {
OutputFormat::Human
};
let other_value = cli.other_flag {
...
} else {
...
}
...
}
Then our main
function would get cluttered over the time.
I'm open for discussion but the arguments you've provided don't really convince me 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer the solution that @franciszekjob proposed, it takes away logic from main function, let's stick to it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Responded to comments
@@ -154,6 +127,14 @@ impl From<Value> for OutputData { | |||
} | |||
} | |||
|
|||
impl<T: CommandResponse + Serialize> From<&T> for OutputData { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CommandResponse + Serialize = CommandResponse
Is it correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's used in text
and json
in SncastMessage
@@ -63,6 +64,7 @@ pub async fn deploy( | |||
keystore_path_, | |||
) | |||
.await | |||
.map(Into::into) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we get rid of this map somehow?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that now we return a separate type AccountDeployResponse
which is constructed from InvokeResponse
, hence the usage of into.
|
||
/// Get the output format of this [`UI`] instance. | ||
#[must_use] | ||
pub fn output_format(&self) -> OutputFormat { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like how we are using this. We are doing ui.output_format() -> format message according to output_format -> print message. The flow should be i give message to ui and it knows how to format it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the idea is you create a UI instance per output format (json or human readable) and you pass it through the application. To me it makes sense that the UI has a (immutable) state like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#[must_use] | ||
pub fn from_flag(json: bool) -> Self { | ||
if json { | ||
OutputFormat::Json | ||
} else { | ||
OutputFormat::Human | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's unnecessary indirection for something this simple. The usage you posted is obvious but from other places, it's not obvious at all what from_flag
method does since arguments name aren't easily visible to the caller.
Why not
let output_format = if cli.json {
OutputFormat::Json
} else {
OutputFormat::Human
};
at the caller site? We aren't saving any code from doing this.
|
||
/// Get the output format of this [`UI`] instance. | ||
#[must_use] | ||
pub fn output_format(&self) -> OutputFormat { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the idea is you create a UI instance per output format (json or human readable) and you pass it through the application. To me it makes sense that the UI has a (immutable) state like this.
crates/foundry-ui/src/message.rs
Outdated
impl Message for &str { | ||
fn text(&self) -> String { | ||
(*self).to_string() | ||
} | ||
} | ||
|
||
impl Message for String { | ||
fn text(&self) -> String { | ||
self.to_string() | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does that play with the default conversion to json? The user will just get the encoded string?
Not sure how we can enforce this on the type level (or if), but for json output maybe it makes sense to return objects always?
What I mean is that maybe there is a reason for the output to always be of the format
SomeObject {
"key1": "value1"
}
and with these default impls the user will get just "string text"
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I mean is that maybe there is a reason for the output to always be of the format
You mean that json should be pretty-printed? If so, I don't think we really need to introduce this. Keep in mind that we've been always returning outputs in json format not pretty printed, so I don't see the reason to change it.
}; | ||
|
||
if let Ok(provider) = explorer.unwrap_or_default().as_provider(network) { | ||
response.print_links(provider); | ||
return Some(response.print_links(provider)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the method should be renamed. It doesn't seem like it is printing anything anymore
#[derive(Debug)] | ||
pub struct UI { | ||
output_format: OutputFormat, | ||
// TODO(3395): Add state here, that can be used for spinner | ||
} | ||
|
||
impl UI { | ||
/// Create a new [`UI`] instance configured with the given output format. | ||
#[must_use] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that I think of it, we should probably define a trait for UI and implement it here.
So you can type our function as accepting the UI trait, not the specific implementation of it.
The benefit will be you will be able to mock the UI in tests or provide an implementation that allows us to just capture what is printed so we can assert it (e.g. for tests that show warnings about snforge_std
).
Towards #3022
Stack:
docs
crate #3409shared
#3405Introduced changes
foundry-ui
crateUI
,Message
Checklist
CHANGELOG.md