actually it is a wrapper to a c-api and when the manager gets released all documents need to be released aswell (of course), maybe in rust I need to think differently and the manager should not have a weak ref (when thinking about it...).
basically my open_document also takes a string/pathbuf and returns a result and when the document will be opened I create a struct with the "raw" pointer and return it, else I will return Err.
the document can than be attached to a operation a operation will be created with the manager. it's a bit of a hazzle to use the api correctly, it's probably hard as a rust beginner to think how I can solve it.
Here is a small diagramm that shows how it works:
i.e. if manager goes out of scope (via drop) it should cleanup all operations and documents, if document goes out of scope it should remove itself from the operation (if it is attached to one)
if a operation goes out of scope basically nothing happens.
Edit: small info:
I'm coming from C# so I'm still have trouble with Rc/Weak and Ownership/Lifetime, basically in C# I have a class Manager which has a HashMap<IntPtr, WeakRef> to Operations and Documents and cleans them up inside Dispose (like Drop), maybe I try to hard to mimic that and my idea was that the Drop function will also free the memory of the document ptr (the document struct only has methods that return Result, so if the ptr is null i can return an err)
Edit2:
when thinking about the answer's I think I made a big mistake to mimic the C# api too much since, rusts lifetime probably will do it for me if the document contains a ref to the manager but the manager does not need to know about documents since they need to go out of scope before the manager, silly me.
prob as easy as:
pub struct DocumentManager {
lock: RwLock<()>,
native_ptr
}
impl DocumentManager {
pub fn new() -> Result<DocumentManager> {
let err = unsafe { };
if err < native_err {
return Err(Error { error_code: err });
}
Ok(DocumentManager {
lock: RwLock::new(()),
})
}
pub fn open_document<'a, T: Into<PathBuf>>(
&'a self,
file_path: T,
password: Option<&str>,
) -> Result<Document<'a>> {
let _locked = self.lock.read().unwrap();
let document = Document::new(
self,
file_path
.into()
.to_str()
.ok_or(Error { error_code: -500 })?,
password,
)?;
Ok(document)
}
}
impl Drop for DocumentManager {
fn drop(&mut self) {
let _locked = self.lock.write().unwrap();
unsafe { ... };
}
}
pub struct Document<'a> {
manager: &'a DocumentManager,
lock: RwLock<()>,
handle: native_ptr,
}
impl<'a> Document<'a> {
fn new(
manager: &'a DocumentManager,
file_name: &str,
password: Option<&str>,
) -> Result<Document<'a>> {
let c_file_name = CString::new(file_name)?;
let mut pdf_document = MaybeUninit::<native_ptr>::uninit();
let err: native_err;
unsafe {
...
};
if err < native_err {
return Err(Error { error_code: err });
}
Ok(Document {
manager,
lock: RwLock::new(()),
handle: unsafe { pdf_document.assume_init() },
})
}
}
impl<'a> Drop for Document<'a> {
fn drop(&mut self) {
let _locked = self.lock.write().unwrap();
unsafe { ... };
}
}
I'm also unsure about the RwLock if it should contain the native_ptr or if it should stay ()
...
it's probably impossible from multiple threads to deref the manager before the document