binaryninja/collaboration/
folder.rs

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