-
Notifications
You must be signed in to change notification settings - Fork 9
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
feat: added crate to manage .lotties #23
Changes from 7 commits
9bea5a1
8511f93
76f5dc8
54e3891
7ab06c0
9f3afcb
e4bc8b8
227fa54
9fe9885
8635dfc
aed8f15
9e1fde5
d557893
994586a
b008511
1225b76
7ced463
5bf9bbb
4b45d16
4bd33a9
e07a70e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/uniffi-bindings | ||
/build | ||
/target | ||
**/*.rs.bk | ||
Cargo.lock | ||
bin/ | ||
/artifacts |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
[package] | ||
name = "dotlottie_fms" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
[lib] | ||
crate-type = ["staticlib", "cdylib", "rlib"] | ||
name = "dotlottie_fms" | ||
|
||
[dependencies] | ||
zip = { version = "0.6.6", default-features = false, features = ["deflate"] } | ||
thiserror = "1.0.48" | ||
serde_json = "1.0.107" | ||
serde = { version = "1.0.188", features = ["derive"] } | ||
base64 = "0.21.4" | ||
json = "0.12.4" | ||
jzon = "0.12.5" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#[derive(Debug)] | ||
pub struct Animation { | ||
pub id: String, | ||
pub animation_data: String, | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,264 @@ | ||||||
use std::{collections::HashMap, ops::Index}; | ||||||
|
||||||
use crate::{get_manifest, Animation, DotLottieError, Manifest, ManifestAnimation}; | ||||||
|
||||||
pub struct DotLottieManager { | ||||||
current_animation_id: String, | ||||||
manifest: Manifest, | ||||||
zip_data: Vec<u8>, | ||||||
animation_settings_cache: HashMap<String, ManifestAnimation>, | ||||||
animation_data_cache: HashMap<String, String>, | ||||||
} | ||||||
|
||||||
impl DotLottieManager { | ||||||
pub fn new(dotlottie: Option<Vec<u8>>) -> Self { | ||||||
if let Some(dotlottie) = dotlottie { | ||||||
// Initialize the manager with the dotLottie file | ||||||
let manifest = get_manifest(&dotlottie); | ||||||
|
||||||
match manifest { | ||||||
Ok(manifest) => { | ||||||
let mut id = String::new(); | ||||||
|
||||||
if let Some(first_animation) = &manifest.active_animation_id { | ||||||
id = first_animation.clone(); | ||||||
} else if let Ok(animations) = manifest.animations.read() { | ||||||
id = animations.index(0).id.clone(); | ||||||
} else { | ||||||
panic!("No animations found in dotLottie file"); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's generally better to return an error instead of panicking as panics are not recoverable |
||||||
} | ||||||
|
||||||
DotLottieManager { | ||||||
current_animation_id: id, | ||||||
manifest, | ||||||
zip_data: dotlottie, | ||||||
animation_settings_cache: HashMap::new(), | ||||||
animation_data_cache: HashMap::new(), | ||||||
} | ||||||
} | ||||||
Err(error) => { | ||||||
eprintln!("Unable to initialize dotLottie manager: {}", error); | ||||||
|
||||||
DotLottieManager { | ||||||
current_animation_id: String::new(), | ||||||
manifest: Manifest::new(), | ||||||
zip_data: vec![], | ||||||
animation_settings_cache: HashMap::new(), | ||||||
animation_data_cache: HashMap::new(), | ||||||
} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if the new returns a Result, we can just propagate the error here. |
||||||
} | ||||||
} | ||||||
} else { | ||||||
DotLottieManager { | ||||||
current_animation_id: String::new(), | ||||||
manifest: Manifest::new(), | ||||||
zip_data: vec![], | ||||||
animation_settings_cache: HashMap::new(), | ||||||
animation_data_cache: HashMap::new(), | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
pub fn init(&mut self, dotlottie: Vec<u8>) { | ||||||
// Initialize the manager with the dotLottie file | ||||||
let manifest = get_manifest(&dotlottie); | ||||||
|
||||||
match manifest { | ||||||
Ok(manifest) => { | ||||||
let mut id = String::new(); | ||||||
|
||||||
if let Some(first_animation) = &manifest.active_animation_id { | ||||||
id = first_animation.clone(); | ||||||
} else if let Ok(animations) = manifest.animations.read() { | ||||||
id = animations.index(0).id.clone(); | ||||||
} else { | ||||||
panic!("No animations found in dotLottie file"); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as previous comment |
||||||
} | ||||||
|
||||||
self.current_animation_id = id; | ||||||
self.manifest = manifest; | ||||||
self.zip_data = dotlottie; | ||||||
} | ||||||
Err(error) => { | ||||||
eprintln!("Unable to initialize dotLottie manager: {}", error); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as previous comment |
||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
/// Advances to the next animation and returns it's animation data as a string. | ||||||
fn next_animation(&mut self) -> Result<String, DotLottieError> { | ||||||
let mut i = 0; | ||||||
let mut new_current_animation_id = self.current_animation_id.clone(); | ||||||
let animations = match self.manifest.animations.read() { | ||||||
Ok(animations) => animations, | ||||||
Err(_) => return Err(DotLottieError::MutexLockError), | ||||||
}; | ||||||
|
||||||
for anim in animations.iter() { | ||||||
if anim.id == self.current_animation_id { | ||||||
if i + 1 < animations.len() { | ||||||
self.current_animation_id = animations[i + 1].id.clone(); | ||||||
|
||||||
new_current_animation_id = animations[i + 1].id.clone(); | ||||||
|
||||||
std::mem::drop(animations); | ||||||
|
||||||
return self.get_animation(&new_current_animation_id); | ||||||
} | ||||||
} | ||||||
i += 1; | ||||||
} | ||||||
|
||||||
std::mem::drop(animations); | ||||||
|
||||||
let current_animation_id = self.current_animation_id.clone(); | ||||||
|
||||||
return self.get_animation(¤t_animation_id); | ||||||
} | ||||||
|
||||||
/// Reverses to the previous animation and returns it's animation data as a string. | ||||||
fn previous_animation(&mut self) -> Result<String, DotLottieError> { | ||||||
let mut new_current_animation_id = self.current_animation_id.clone(); | ||||||
let animations = match self.manifest.animations.read() { | ||||||
Ok(animations) => animations, | ||||||
Err(_) => return Err(DotLottieError::MutexLockError), | ||||||
}; | ||||||
let mut i = 0; | ||||||
|
||||||
for anim in animations.iter() { | ||||||
if anim.id == self.current_animation_id { | ||||||
if i > 0 { | ||||||
self.current_animation_id = animations[i - 1].id.clone(); | ||||||
|
||||||
new_current_animation_id = animations[i - 1].id.clone(); | ||||||
std::mem::drop(animations); | ||||||
|
||||||
return self.get_animation(&new_current_animation_id); | ||||||
} | ||||||
} | ||||||
i += 1; | ||||||
} | ||||||
|
||||||
std::mem::drop(animations); | ||||||
|
||||||
let current_animation_id = self.current_animation_id.clone(); | ||||||
|
||||||
self.get_animation(¤t_animation_id) | ||||||
} | ||||||
|
||||||
/// Returns the playback settings for the animation with the given ID. | ||||||
/// Memoizes the settings in a HashMap for faster access. | ||||||
pub fn get_playback_settings( | ||||||
&mut self, | ||||||
animation_id: &str, | ||||||
) -> Result<ManifestAnimation, DotLottieError> { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. from the implementation, i notice that the animation_id may not exists in the map and that would return an Error, while we should return a more appropriate value to the consumer.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @theashraf if the animation_id isnt present we return an error, otherwise it will be there so we return a manifestAnimation, dont see why it needs to be an option in that case? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @samuelOsborne currently it doesn't return a manifestAnimation not found error, instead it returns a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed it to return AnimationNotFound if its not there |
||||||
if let Some(manifest_animation) = self.animation_settings_cache.get(animation_id) { | ||||||
let cloned_animation = manifest_animation.clone(); // Clone the value | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
return Result::Ok(cloned_animation); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
if let Ok(animations) = self.manifest.animations.read() { | ||||||
for anim in animations.iter() { | ||||||
if &anim.id == animation_id { | ||||||
self.animation_settings_cache | ||||||
.insert(animation_id.to_string().clone(), anim.clone()); | ||||||
|
||||||
return Result::Ok(anim.clone()); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
return Result::Err(DotLottieError::MutexLockError); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
pub fn contains_animation(&self, animation_id: &str) -> Result<bool, DotLottieError> { | ||||||
if let Ok(animations) = self.manifest.animations.read() { | ||||||
for anim in animations.iter() { | ||||||
if anim.id == animation_id { | ||||||
return Result::Ok(true); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
} | ||||||
|
||||||
return Result::Ok(false); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
return Result::Err(DotLottieError::MutexLockError); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
pub fn get_current_animation(&mut self) -> Result<String, DotLottieError> { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
let current_animation_id = self.current_animation_id.clone(); | ||||||
|
||||||
self.get_animation(¤t_animation_id) | ||||||
} | ||||||
|
||||||
pub fn current_animation_playback_settings( | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
&mut self, | ||||||
) -> Result<ManifestAnimation, DotLottieError> { | ||||||
let animation_id = self.current_animation_id.clone(); | ||||||
|
||||||
self.get_playback_settings(&animation_id) | ||||||
} | ||||||
|
||||||
/// Returns the animation data for the animation with the given ID. | ||||||
/// Memoizes the animation data in a HashMap for faster access. | ||||||
pub fn get_animation(&mut self, animation_id: &str) -> Result<String, DotLottieError> { | ||||||
if let Some(animation) = self.animation_data_cache.get(animation_id) { | ||||||
let cloned_animation = animation.clone(); // Clone the value | ||||||
|
||||||
return Result::Ok(cloned_animation); | ||||||
} else { | ||||||
let animation = crate::get_animation(&self.zip_data, animation_id); | ||||||
|
||||||
if let Ok(animation) = animation { | ||||||
self.animation_data_cache | ||||||
.insert(animation_id.to_string().clone(), animation.clone()); | ||||||
|
||||||
return Result::Ok(animation); | ||||||
} else { | ||||||
return Result::Err(DotLottieError::AnimationNotFound { | ||||||
animation_id: animation_id.to_string(), | ||||||
}); | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
pub fn get_animations(&self) -> Result<Vec<Animation>, DotLottieError> { | ||||||
crate::get_animations(&self.zip_data) | ||||||
} | ||||||
|
||||||
pub fn set_active_animation(&mut self, animation_id: &str) -> Result<String, DotLottieError> { | ||||||
if let Ok(contains) = self.contains_animation(animation_id) { | ||||||
if contains { | ||||||
self.current_animation_id = animation_id.to_string(); | ||||||
|
||||||
return Result::Ok(self.get_animation(animation_id)?); | ||||||
} | ||||||
} | ||||||
|
||||||
return Result::Err(DotLottieError::AnimationNotFound { | ||||||
animation_id: animation_id.to_string(), | ||||||
}); | ||||||
} | ||||||
|
||||||
pub fn manifest(&self) -> Manifest { | ||||||
let mut manifest = Manifest::new(); | ||||||
|
||||||
manifest.active_animation_id = Some(self.current_animation_id.clone()); | ||||||
manifest.animations = self.manifest.animations.read().unwrap().clone().into(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. may panic!, i think we can update the return of the manifest function to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @theashraf what part of this function could cause error? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @samuelOsborne |
||||||
manifest.author = self.manifest.author.clone(); | ||||||
manifest.description = self.manifest.description.clone(); | ||||||
manifest.generator = self.manifest.generator.clone(); | ||||||
manifest.keywords = self.manifest.keywords.clone(); | ||||||
manifest.revision = self.manifest.revision; | ||||||
manifest.themes = self.manifest.themes.clone(); | ||||||
manifest.states = self.manifest.states.clone(); | ||||||
manifest.version = self.manifest.version.clone(); | ||||||
|
||||||
manifest | ||||||
} | ||||||
|
||||||
pub fn current_animation_id(&self) -> String { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
self.current_animation_id.clone() | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
use thiserror; | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
pub enum DotLottieError { | ||
#[error("Failed to open archive")] | ||
ArchiveOpenError, | ||
|
||
#[error("Unable to find the file: {file_name}")] | ||
FileFindError { file_name: String }, | ||
|
||
#[error("Unable to read the contents")] | ||
ReadContentError, | ||
|
||
#[error("Unable to lock the animations mutex")] | ||
MutexLockError, | ||
|
||
#[error("Animation not found")] | ||
AnimationNotFound { animation_id: String }, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think both hash maps would hold the same keys, maybe we can combine both together in a single HashMap, where the value is a tuple ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what would the String in the tuple contain?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@samuelOsborne it would contain the what this HashMap holds as a value
animation_data_cache: HashMap<String, String>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@theashraf Im going to create a ticket for this refactoring as at the moment having a single map containing both creates an endless loop inserting in to the map as both functions (getting playback setting, animation_data) will depend on each other
get an animationData -> needs to get the ManifestAnimation -> needs to get animation
get ManifestAnimation -> needs to get animationData -> needs to get ManifestAnimation