Skip to content

Conversation

pjungkamp
Copy link
Contributor

Add a manual override to use glib::ExitCode instead of the gir generated i32 return value for connect_command_line and connect_handle_local_options.

This fixes #1691

bilelmoussaoui
bilelmoussaoui previously approved these changes Mar 30, 2025
}

#[doc(alias = "handle-local-options")]
fn connect_handle_local_options<F: Fn(&Self, &glib::VariantDict) -> ExitCode + 'static>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #1592

Also CC @A6GibKm

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add another associated constant to glib::ExitCode, e.g. glib::ExitCode::CONTINUE? I dont feel comfortable with an enum or e.g. Option because Some(glib::ExitCode::from(-1)) and None would be two representations for the effectively the same value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just checked the documentation for the return value of the handle-local-options signal.

An exit code. If you have handled your options and want to exit the process, return a non-negative option, 0 for success, and a positive value for failure. To continue, return -1 to let the default option processing continue.

It seems like the definition of glib::ExitCode should be:

  • positive means failure
  • zero means success
  • negative means special value

This means that I'm really not comfortable with the implementation of Termination for glib::ExitCode. Any value that does not fit into u8 shoudl probably be mapped to e.g. u8::MAX.

I've pushed an additional commit that adds a glib::ExitCode::CONTINUE associated constant and some initial documentation for the glib::ExitCode struct.

Copy link
Contributor

@A6GibKm A6GibKm Apr 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like the definition of glib::ExitCode should be:

That proposal only makes sense if the negative values have a meaning whenever they are used. As far as I have seen it is only a handful of fns that do something special with negative values. Please correct me if I am wrong.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like @A6GibKm says, the main problem here is that handle_local_options() is special. It should probably have a different type than the other cases.

Copy link
Contributor Author

@pjungkamp pjungkamp Apr 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't like to have multiple representations for the same handling. If we'd use Option<glib::ExitCode> there shouldn't be a way to say Some(glib::ExitCode::from(-1)) and None which would lead to the same effect.

We could constrain the glib::ExitCode type though.

  • Remove impl From<i32> for glib::ExitCode
  • Add impl From<u8> for glib::ExitCode instead
  • Add impl TryFrom<i32> for glib::ExitCode
  • Add struct InvalidExitCode(i32) as the TryFrom::Error type

We could then modify interfaces that currently return a glib::ExitCode which may originate from foreign code, e.g. gio::ApplicationExtManual::run to return a Result<glib::ExitCode, glib::InvalidExitCode> instead. While we'd enforce good exit codes from Rust code through the traits and conversion methods. Using Option<glib::ExitCode> or std::ops::ControlFlow<glib::ExitCode> would then be fine for handle-local-options.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this implementation makes sense. @A6GibKm what do you think?

@pjungkamp pjungkamp force-pushed the gio/application-command-line branch from c7a63e0 to e498ede Compare March 31, 2025 10:31
@pjungkamp pjungkamp force-pushed the gio/application-command-line branch 2 times, most recently from 2bdf6e6 to f99d9a6 Compare April 7, 2025 11:52
@pjungkamp
Copy link
Contributor Author

pjungkamp commented Apr 7, 2025

I've now changed the handle-local-options signal and local-command-line virtual function to use std::ops::ControlFlow<ExitCode>. ExitCode now contains a u8 and only implements TryFrom<i32> instead of From<i32>.

One benefit of std::ops::ControlFlow<ExitCode> over Option<ExitCode> is the handling with the ? operator.

This required me to bump the MSRV to 1.83.

Using Option<ExitCode>

impl ApplicationExt for MyApplication {
    fn local_command_line(&self, arguments: &mut ArgumentList) -> Option<ExitCode> {
        // The `?` operator won't work here since it returns on `None` 
        if let Some(exit_code) = self.parent_local_command_line(arguments) {
            return Some(exit_code);
        }
        
        // Handle my own options
        
        None // Continue with default handling
    }
}

Using ControlFlow<ExitCode>

impl ApplicationExt for MyApplication {
    fn local_command_line(&self, arguments: &mut ArgumentList) -> ControlFlow<ExitCode> {
        // This will return if the parent class has handled the entire command line. 
        self.parent_local_command_line(arguments)?;
        
        // Handle my own options
        
        ControlFlow::Continue(()) // Continue with default handling
    }
}

@pjungkamp pjungkamp force-pushed the gio/application-command-line branch from f99d9a6 to 2cb2b35 Compare April 7, 2025 12:02
@pjungkamp pjungkamp marked this pull request as draft April 7, 2025 17:14
@pjungkamp pjungkamp force-pushed the gio/application-command-line branch 2 times, most recently from caae6ea to 0ba97c1 Compare April 8, 2025 10:47
Add a manual override to use `glib::ExitCode` or `ControlFlow<glib::ExitCode>`
instead of the gir generated `i32` return value for the `command-line`
and `handle-local-options` signals as well as the `local_command_line`
virtual function.

This reworks the `ExitCode` type to only support `u8` values. The `From<i32>`
implementation is replaced with a `TryFrom<i32>` implementation.
@sdroege sdroege force-pushed the gio/application-command-line branch from 36c7e6c to 218ed47 Compare April 13, 2025 07:17
@sdroege
Copy link
Member

sdroege commented Apr 13, 2025

Let's get this in then

@sdroege sdroege marked this pull request as ready for review April 13, 2025 07:21
@sdroege sdroege merged commit d3dc994 into gtk-rs:main Apr 13, 2025
48 checks passed
@swsnr
Copy link
Contributor

swsnr commented Jul 16, 2025

@pjungkamp @sdroege While upgrading I came across this change and noticed that while this PR changes some parts of the command line API to use ExitCode, ApplicationCommandline::set_exit_code still takes an i32 instead of an ExitCode.

Shouldn't it now take ExitCode as well? (Though, arguably, that'd be a breaking change now).

@sdroege
Copy link
Member

sdroege commented Jul 18, 2025

@pjungkamp Maybe we can get around that by changing this to impl TryInto<ExitCode> or so and map errors to ExitCode::FAILURE? But yes, that should've been changed too. Do you want to provide a PR?

Alternatively could add a set_exit_code_what_do_we_name_it() :)

@swsnr
Copy link
Contributor

swsnr commented Jul 18, 2025

@sdroege Wrt to a PR, are you asking me or @pjungkamp? I can make a PR, but I'd not bother much with backwards compatibility, and just change the API right away. Then you can either release a quick 0.21.1 with this change; even though it's technically breaking, I doubt it has much practical impact, since it's a bit of a niche API, I guess. Or you just save the change for 0.22; effectively it only saves one .into() call anyway 🙂

@pjungkamp
Copy link
Contributor Author

I agree that the gio::ApplicationCommandlLineExt::{exit_status,set_exit_status} APIs are a good fit for the glib::ExitCode type since it's used for process termination.

The Rust standard library uses the ExitStatus type for values from wait and the ExitCode type for process termination.

We could follow this naming convention in std (Status vs. Code) and introduce gio::ApplicationCommandLineExt::{set_exit_code,exit_code} methods and mark the exit_status ones as deprecated and remove them with the next minor version bump. Other APIs like glib::spawn_check_wait_status and gio::Subprocess::exit_status would already match this convention.

@sdroege
Copy link
Member

sdroege commented Aug 5, 2025

That sounds good to me. Do you want to provide a PR @pjungkamp ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE REQUEST] gio::Application signal handlers should return glib::ExitCode instead of i32

5 participants