Skip to content
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

Basic version of NSError #16

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Conversation

rnapier
Copy link

@rnapier rnapier commented Nov 23, 2021

This is not a full PR, but I want to open it up to start talking about it. There are several things in here:

  • Creates an NSError type (which is the piece I'm interested in)
  • Creates a new constant NSLocalizedDescriptionKey(), but this doesn't feel like a great way to define constants (could they be imported from ObjC directly? Could they be const rather than a function?)
  • Creates NSInteger and NSUInteger in runtime.rs. This is a bit confusing, since there is already objc-runtime.

The objc2 repo doesn't seem ready for primetime. Is it better to extend this one or work there? Is there a better approach to all of this? My primary need is to call Rust code from Swift, and I'm looking for how to best do that. Currently I'm using this NSError as follows:

#[no_mangle]
pub extern "C" fn initThing(
    json: &NSData,
    error: *mut *const NSError,
) -> bool {
    if let Err(err) = init_thing(json.bytes()) {
        let value = NSString::from_str(&format!("{:?}", err));
        let user_info =
            NSDictionary::from_keys_and_objects(&[&*NSLocalizedDescriptionKey()], vec![value]);
        unsafe {
            let nserror = NSError::new(0, NSString::from_str("myDomain"), user_info);
            *error = &*nserror;
            mem::forget(nserror);
        }
        false
    } else {
        true
    }
}

I then consume it with this:

// This is a hack around the Swift/ObjC importer not handling top-level functions:
// https://bugs.swift.org/browse/SR-7400
func callRust<T>(_ f: (T, NSErrorPointer) -> Bool, _ p1: T) throws {
    var error: NSError? = nil
    if !f(p1, &error), let error = error {
        throw error
    }
}

class MyThing {
    init() {
        let json = getData()

        do {
            try callRust(initThing, json)
        } catch let error {
            print("FAILED: \(error)")
        }
    }
}

I don't think this is a fully baked approach (and it's very ugly), so I'm looking for direction.

Copy link

@madsmtm madsmtm left a comment

Choose a reason for hiding this comment

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

See also fruity's NSError for inspiration.

I'm not sure how the Swift-side works, but I can imagine a macro that would at least make the Rust-side a bit nicer:

#[no_mangle]
#[my_nserror_macro]
pub extern "C" fn initThing(json: &NSData) -> Result<(), Id<NSError>> {
    if let Err(err) = init_thing(json.bytes()) {
        let value = NSString::from_str(&format!("{:?}", err));
        let user_info = NSDictionary::from_keys_and_objects(&[&*NSLocalizedDescriptionKey()], vec![value]);
        Err(NSError::new(0, NSString::from_str("myDomain"), user_info))
    } else {
        Ok(())
    }
}
// After `my_nserror_macro`
#[no_mangle]
pub extern "C" fn initThing(
    json: &NSData,
    error: *mut *const NSError,
) -> bool {
    // fn inner = The original function
    if let Err(nserror) = inner(json) {
        unsafe {
            *error = &*nserror;
            mem::forget(nserror);
        }
        false
    } else {
        true
    }
}

And maybe a impl<T: std::error::Error> From<T> for Id<NSError> or similar implementation, and vice-versa, would make it even nicer?

Comment on lines +1 to +2
pub type NSInteger = isize;
pub type NSUInteger = usize;
Copy link

Choose a reason for hiding this comment

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

I think these could be useful to have in objc, the reason I haven't added them myself is that I'm yet not completely sure they will always be isize/usize on more exotic platforms like Windows using GNUStep

Comment on lines +6 to +9
#[allow(non_snake_case)]
pub fn NSLocalizedDescriptionKey() -> Id<NSString> {
NSString::from_str(&"NSLocalizedDescription")
}
Copy link

Choose a reason for hiding this comment

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

An alternative would be to use the lazy_static! macro, but I think your approach is fine.

In the future something like fruity's nsstring! could be really nice, but it is very difficult to ensure the correctness of, similar to SSheldon/rust-objc#49.

Copy link

Choose a reason for hiding this comment

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

Also, this actually has value "NSLocalizedDescriptionKey" on GNUStep

@@ -1,3 +1,4 @@
doc/
target/
Cargo.lock
.idea
Copy link

Choose a reason for hiding this comment

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

Consider adding this to your ~/.gitignore_global instead? Unless IntelliJ works differently than how I think it does.

madsmtm added a commit to madsmtm/objc2 that referenced this pull request Jan 12, 2022
Inspiration from SSheldon/rust-objc-foundation#16

Co-authored-by: Rob Napier <rob@neverwood.org>
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.

2 participants