binaryninja/collaboration/
folder.rs

1use super::{Remote, RemoteProject};
2use binaryninjacore_sys::*;
3use std::ptr::NonNull;
4
5use crate::project::folder::ProjectFolder;
6use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
7use crate::string::{BnString, IntoCStr};
8
9#[repr(transparent)]
10pub struct RemoteFolder {
11    pub(crate) handle: NonNull<BNRemoteFolder>,
12}
13
14impl RemoteFolder {
15    pub(crate) unsafe fn from_raw(handle: NonNull<BNRemoteFolder>) -> Self {
16        Self { handle }
17    }
18
19    pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNRemoteFolder>) -> Ref<Self> {
20        Ref::new(Self { handle })
21    }
22
23    // TODO: Rename to local folder?
24    // TODO: Bump this to an option
25    /// Get the core folder associated with this remote folder.
26    pub fn core_folder(&self) -> Result<Ref<ProjectFolder>, ()> {
27        let result = unsafe { BNRemoteFolderGetCoreFolder(self.handle.as_ptr()) };
28        NonNull::new(result)
29            .map(|handle| unsafe { ProjectFolder::ref_from_raw(handle) })
30            .ok_or(())
31    }
32
33    // TODO: Bump this to an option
34    /// Get the owning project of this folder.
35    pub fn project(&self) -> Result<Ref<RemoteProject>, ()> {
36        let result = unsafe { BNRemoteFolderGetProject(self.handle.as_ptr()) };
37        NonNull::new(result)
38            .map(|handle| unsafe { RemoteProject::ref_from_raw(handle) })
39            .ok_or(())
40    }
41
42    // TODO: Bump this to an option
43    /// Get the owning remote of this folder.
44    pub fn remote(&self) -> Result<Ref<Remote>, ()> {
45        let result = unsafe { BNRemoteFolderGetRemote(self.handle.as_ptr()) };
46        NonNull::new(result)
47            .map(|handle| unsafe { Remote::ref_from_raw(handle) })
48            .ok_or(())
49    }
50
51    // TODO: Should this pull folders?
52    // TODO: If it does we keep the result?
53    /// Get the parent folder, if available.
54    pub fn parent(&self) -> Result<Option<Ref<RemoteFolder>>, ()> {
55        let project = self.project()?;
56        // TODO: This sync should be removed?
57        if !project.has_pulled_folders() {
58            project.pull_folders()?;
59        }
60        let mut parent_handle = std::ptr::null_mut();
61        let success = unsafe { BNRemoteFolderGetParent(self.handle.as_ptr(), &mut parent_handle) };
62        success
63            .then(|| {
64                NonNull::new(parent_handle)
65                    .map(|handle| unsafe { RemoteFolder::ref_from_raw(handle) })
66            })
67            .ok_or(())
68    }
69
70    /// Set the parent folder. You will need to push the folder to update the remote version.
71    pub fn set_parent(&self, parent: Option<&RemoteFolder>) -> Result<(), ()> {
72        let parent_handle = parent.map_or(std::ptr::null_mut(), |p| p.handle.as_ptr());
73        let success = unsafe { BNRemoteFolderSetParent(self.handle.as_ptr(), parent_handle) };
74        success.then_some(()).ok_or(())
75    }
76
77    /// Get web API endpoint URL.
78    pub fn url(&self) -> String {
79        let result = unsafe { BNRemoteFolderGetUrl(self.handle.as_ptr()) };
80        assert!(!result.is_null());
81        unsafe { BnString::into_string(result) }
82    }
83
84    /// Get unique ID.
85    pub fn id(&self) -> String {
86        let result = unsafe { BNRemoteFolderGetId(self.handle.as_ptr()) };
87        assert!(!result.is_null());
88        unsafe { BnString::into_string(result) }
89    }
90
91    /// Unique id of parent folder, if there is a parent. None, otherwise
92    pub fn parent_id(&self) -> Option<BnString> {
93        let mut parent_id = std::ptr::null_mut();
94        let have = unsafe { BNRemoteFolderGetParentId(self.handle.as_ptr(), &mut parent_id) };
95        have.then(|| unsafe { BnString::from_raw(parent_id) })
96    }
97
98    /// Displayed name of folder
99    pub fn name(&self) -> String {
100        let result = unsafe { BNRemoteFolderGetName(self.handle.as_ptr()) };
101        assert!(!result.is_null());
102        unsafe { BnString::into_string(result) }
103    }
104
105    /// Set the display name of the folder. You will need to push the folder to update the remote version.
106    pub fn set_name(&self, name: &str) -> Result<(), ()> {
107        let name = name.to_cstr();
108        let success = unsafe { BNRemoteFolderSetName(self.handle.as_ptr(), name.as_ptr()) };
109        success.then_some(()).ok_or(())
110    }
111
112    /// Description of the folder
113    pub fn description(&self) -> String {
114        let result = unsafe { BNRemoteFolderGetDescription(self.handle.as_ptr()) };
115        assert!(!result.is_null());
116        unsafe { BnString::into_string(result) }
117    }
118
119    /// Set the description of the folder. You will need to push the folder to update the remote version.
120    pub fn set_description(&self, description: &str) -> Result<(), ()> {
121        let description = description.to_cstr();
122        let success =
123            unsafe { BNRemoteFolderSetDescription(self.handle.as_ptr(), description.as_ptr()) };
124        success.then_some(()).ok_or(())
125    }
126}
127
128impl PartialEq for RemoteFolder {
129    fn eq(&self, other: &Self) -> bool {
130        self.id() == other.id()
131    }
132}
133impl Eq for RemoteFolder {}
134
135impl ToOwned for RemoteFolder {
136    type Owned = Ref<Self>;
137
138    fn to_owned(&self) -> Self::Owned {
139        unsafe { RefCountable::inc_ref(self) }
140    }
141}
142
143unsafe impl RefCountable for RemoteFolder {
144    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
145        Ref::new(Self {
146            handle: NonNull::new(BNNewRemoteFolderReference(handle.handle.as_ptr())).unwrap(),
147        })
148    }
149
150    unsafe fn dec_ref(handle: &Self) {
151        BNFreeRemoteFolder(handle.handle.as_ptr());
152    }
153}
154
155impl CoreArrayProvider for RemoteFolder {
156    type Raw = *mut BNRemoteFolder;
157    type Context = ();
158    type Wrapped<'a> = Guard<'a, Self>;
159}
160
161unsafe impl CoreArrayProviderInner for RemoteFolder {
162    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
163        BNFreeRemoteFolderList(raw, count)
164    }
165
166    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
167        let raw_ptr = NonNull::new(*raw).unwrap();
168        Guard::new(Self::from_raw(raw_ptr), context)
169    }
170}