From 58b5e235ef9c1387f9c4ee3a8cbaf1a383b34777 Mon Sep 17 00:00:00 2001 From: tami5 Date: Sun, 12 Jun 2022 17:37:14 +0300 Subject: [PATCH] ref(pbxproj): abstract PBXFileReference & PBX*Group into one type --- src/pbxproj/object/build/phase/kind.rs | 2 +- src/pbxproj/object/collection.rs | 30 ++- src/pbxproj/object/file/element.rs | 163 ---------------- src/pbxproj/object/file/group.rs | 127 ------------ src/pbxproj/object/file/mod.rs | 9 - src/pbxproj/object/file/reference.rs | 49 ----- src/pbxproj/object/file/source_tree.rs | 57 ------ src/pbxproj/object/fs/kind.rs | 54 ++++++ src/pbxproj/object/fs/mod.rs | 136 +++++++++++++ src/pbxproj/object/fs/obj.rs | 48 +++++ src/pbxproj/object/fs/setget.rs | 258 +++++++++++++++++++++++++ src/pbxproj/object/fs/source_tree.rs | 64 ++++++ src/pbxproj/object/kind.rs | 50 +++-- src/pbxproj/object/meta.rs | 17 +- src/pbxproj/object/mod.rs | 2 + src/pbxproj/object/project.rs | 20 +- 16 files changed, 628 insertions(+), 458 deletions(-) delete mode 100644 src/pbxproj/object/file/element.rs delete mode 100644 src/pbxproj/object/file/group.rs delete mode 100644 src/pbxproj/object/file/reference.rs delete mode 100644 src/pbxproj/object/file/source_tree.rs create mode 100644 src/pbxproj/object/fs/kind.rs create mode 100644 src/pbxproj/object/fs/mod.rs create mode 100644 src/pbxproj/object/fs/obj.rs create mode 100644 src/pbxproj/object/fs/setget.rs create mode 100644 src/pbxproj/object/fs/source_tree.rs diff --git a/src/pbxproj/object/build/phase/kind.rs b/src/pbxproj/object/build/phase/kind.rs index e7eef87..a5e43a4 100644 --- a/src/pbxproj/object/build/phase/kind.rs +++ b/src/pbxproj/object/build/phase/kind.rs @@ -21,7 +21,7 @@ pub enum PBXBuildPhaseKind { } impl PBXBuildPhaseKind { - /// Return string represntation of PBXBuildPhaseKind + /// Return string representation of PBXBuildPhaseKind pub fn as_isa(&self) -> &str { match self { Self::Headers => "PBXHeadersBuildPhase", diff --git a/src/pbxproj/object/collection.rs b/src/pbxproj/object/collection.rs index aa5c5e3..a918617 100644 --- a/src/pbxproj/object/collection.rs +++ b/src/pbxproj/object/collection.rs @@ -2,7 +2,7 @@ use md5::Digest; use super::*; use std::{ - cell::RefCell, + cell::{Ref, RefCell}, collections::HashMap, rc::{Rc, Weak}, }; @@ -58,14 +58,27 @@ impl PBXObjectCollection { .collect() } - /// Get all PBXGroup - pub fn groups<'a>(&'a self) -> Vec<(String, Rc>)> { + fn fs_references<'a>( + &'a self, + predict: fn(Ref) -> bool, + ) -> Vec<(String, Rc>)> { self.iter() - .filter(|o| o.1.is_pbx_group()) - .map(|(k, o)| (k.clone(), o.as_pbx_group().unwrap().clone())) + .filter(|o| { + if let Some(fs_reference) = o.1.as_pbxfs_reference() { + predict(fs_reference.borrow()) + } else { + false + } + }) + .map(|(k, o)| (k.clone(), o.as_pbxfs_reference().unwrap().clone())) .collect() } + /// Get all PBXGroup + pub fn groups<'a>(&'a self) -> Vec<(String, Rc>)> { + self.fs_references(|fs_reference| fs_reference.is_group()) + } + /// Get all PBXProject pub fn projects<'a>(&'a self) -> Vec<(String, Rc>)> { self.iter() @@ -75,11 +88,8 @@ impl PBXObjectCollection { } /// Get all files - pub fn files<'a>(&'a self) -> Vec<(String, Rc>)> { - self.iter() - .filter(|o| o.1.is_pbx_file_reference()) - .map(|(k, o)| (k.clone(), o.as_pbx_file_reference().unwrap().clone())) - .collect() + pub fn files<'a>(&'a self) -> Vec<(String, Rc>)> { + self.fs_references(|fs_reference| fs_reference.is_file()) } /// Get all PBXBuildFile diff --git a/src/pbxproj/object/file/element.rs b/src/pbxproj/object/file/element.rs deleted file mode 100644 index e81f280..0000000 --- a/src/pbxproj/object/file/element.rs +++ /dev/null @@ -1,163 +0,0 @@ -use crate::pbxproj::*; - -/// `Abstraction` of file and group elements -/// -/// Used by: [`PBXFileReference`] and [`PBXGroup`] -/// -/// [`PBXFileReference`]: crate::pbxproj::PBXFileReference -/// [`PBXGroup`]: crate::pbxproj::PBXGroup -#[derive(Debug, Default, derive_new::new)] -/// This element is an abstract parent for file and group elements. -pub struct PBXFileElement { - /// Element source tree. - pub source_tree: Option, - /// Element path. - pub path: Option, - /// Element name. - pub name: Option, - /// Element include in index. - pub include_in_index: Option, - /// Element uses tabs. - pub uses_tabs: Option, - /// Element indent width. - pub indent_width: Option, - /// Element tab width. - pub tab_width: Option, - /// Element wraps lines. - pub wraps_lines: Option, - /// Element parent. - pub(crate) parent_reference: Option, - objects: WeakPBXObjectCollection, -} - -impl PBXObjectExt for PBXFileElement { - fn from_hashmap(mut value: PBXHashMap, objects: WeakPBXObjectCollection) -> anyhow::Result - where - Self: Sized, - { - Ok(Self { - source_tree: value.remove_string("sourceTree").map(|s| s.into()), - path: value.remove_string("path"), - name: value.remove_string("name"), - include_in_index: value.remove_number("includeInIndex").map(|v| v == 1), - uses_tabs: value.remove_number("usesTabs").map(|v| v == 1), - indent_width: value.remove_number("indentWidth"), - tab_width: value.remove_number("tabWidth"), - wraps_lines: value.remove_number("wrapsLines").map(|v| v == 1), - parent_reference: None, - objects, - }) - } - - fn to_hashmap(&self) -> PBXHashMap { - todo!() - } -} - -impl PBXFileElement { - /// Get a reference to the pbxfile element's parent reference. - #[must_use] - pub fn parent(&self, _data: PBXRootObject) -> Option<&PBXFileElement> { - todo!() - // self.parent_reference.as_ref() - } - - /// Set the pbxfile element's parent reference. - pub fn set_parent_reference(&mut self, parent_reference: Option) { - self.parent_reference = parent_reference; - } - - /// Set the pbxfile element's source tree. - pub fn set_source_tree(&mut self, source_tree: Option) { - self.source_tree = source_tree; - } - - /// Get a reference to the pbxfile element's source tree. - #[must_use] - pub fn source_tree(&self) -> Option<&PBXFileSourceTree> { - self.source_tree.as_ref() - } - - /// Set the pbxfile element's path. - pub fn set_path(&mut self, path: Option) { - self.path = path; - } - - /// Get a reference to the pbxfile element's path. - #[must_use] - pub fn path(&self) -> Option<&String> { - self.path.as_ref() - } - - /// Set the pbxfile element's name. - pub fn set_name(&mut self, name: Option) { - self.name = name; - } - - /// Get a reference to the pbxfile element's name. - #[must_use] - pub fn name(&self) -> Option<&String> { - self.name.as_ref() - } - - /// Set the pbxfile element's include in index. - pub fn set_include_in_index(&mut self, include_in_index: Option) { - self.include_in_index = include_in_index; - } - - /// Get the pbxfile element's include in index. - #[must_use] - pub fn include_in_index(&self) -> Option { - self.include_in_index - } - - /// Set the pbxfile element's uses tabs. - pub fn set_uses_tabs(&mut self, uses_tabs: Option) { - self.uses_tabs = uses_tabs; - } - - /// Get the pbxfile element's uses tabs. - #[must_use] - pub fn uses_tabs(&self) -> Option { - self.uses_tabs - } - - /// Set the pbxfile element's indent width. - pub fn set_indent_width(&mut self, indent_width: Option) { - self.indent_width = indent_width; - } - - /// Get the pbxfile element's indent width. - #[must_use] - pub fn indent_width(&self) -> Option { - self.indent_width - } - - /// Set the pbxfile element's tab width. - pub fn set_tab_width(&mut self, tab_width: Option) { - self.tab_width = tab_width; - } - - /// Get the pbxfile element's tab width. - #[must_use] - pub fn tab_width(&self) -> Option { - self.tab_width - } - - /// Set the pbxfile element's wraps lines. - pub fn set_wraps_lines(&mut self, wraps_lines: Option) { - self.wraps_lines = wraps_lines; - } - - /// Get the pbxfile element's wraps lines. - #[must_use] - pub fn wraps_lines(&self) -> Option { - self.wraps_lines - } - - /// Get a reference to the pbxfile element's parent reference. - #[must_use] - pub fn parent_reference(&self) -> Option<&String> { - self.parent_reference.as_ref() - } -} diff --git a/src/pbxproj/object/file/group.rs b/src/pbxproj/object/file/group.rs deleted file mode 100644 index d78a637..0000000 --- a/src/pbxproj/object/file/group.rs +++ /dev/null @@ -1,127 +0,0 @@ -use crate::pbxproj::*; -use derive_deref_rs::Deref; -use std::collections::HashSet; - -/// [`PBXObject`] representing a collection of files in Xcode's VF hierarchy. -/// -/// [`PBXObject`]: crate::pbxproj::PBXObject -#[derive(Debug, derive_new::new)] -pub struct PBXGroup { - /// Group children references. - children_references: HashSet, - inner: PBXFileElement, -} - -impl PBXGroup { - /// Group children. - pub fn set_children_references(&mut self, references: HashSet) -> HashSet { - let old = std::mem::replace(&mut self.children_references, references); - old - } - - // /// Group children. - // pub fn children<'a>(&'a self, data: &'a PBXRootObject) -> Vec> { - // self.children_references - // .iter() - // .map(|r| Some(data.get(r)?.borrow())) - // .flatten() - // .collect::>() - // } - - /// Get a reference to the pbxgroup's children references. - #[must_use] - pub fn children_references(&self) -> &HashSet { - &self.children_references - } -} - -impl PBXObjectExt for PBXGroup { - fn from_hashmap(mut value: PBXHashMap, objects: WeakPBXObjectCollection) -> anyhow::Result - where - Self: Sized, - { - Ok(Self { - children_references: HashSet::from_iter( - value.try_remove_vec("children")?.try_into_vec_strings()?, - ), - inner: PBXObjectExt::from_hashmap(value, objects)?, - }) - } - - fn to_hashmap(&self) -> PBXHashMap { - todo!() - } -} - -/// Group Adding option variants -pub enum PBXGroupAddingOption { - /// Group without a folder - WithoutFolder, -} - -/// [`PBXObject`] specifying [`PBXGroup`] representing localized resources -/// -/// [`PBXObject`]: crate::pbxproj::PBXObject -#[derive(Debug, Deref, derive_new::new)] -pub struct PBXVariantGroup { - inner: PBXGroup, -} - -impl PBXObjectExt for PBXVariantGroup { - fn from_hashmap(value: PBXHashMap, objects: WeakPBXObjectCollection) -> anyhow::Result - where - Self: Sized, - { - Ok(Self { - inner: PBXObjectExt::from_hashmap(value, objects)?, - }) - } - - fn to_hashmap(&self) -> PBXHashMap { - todo!() - } -} - -/// [`PBXObject`] specifying [`PBXGroup`] containing different versions of a resource -/// -/// [`PBXObject`]: crate::pbxproj::PBXObject -#[derive(Debug, Deref, derive_new::new)] -pub struct XCVersionGroup { - /// Current version. - current_version_reference: Option, - /// Version group type. - pub version_group_type: Option, - #[deref] - inner: PBXGroup, -} - -impl XCVersionGroup { - /// Returns the current version file reference. - pub fn current_version(&self, _store: &PBXRootObject) -> Option { - // currentVersionReference?.getObject() - todo!() - } - - /// Set current version reference - pub fn set_current_version_reference(&mut self, value: Option) -> Option { - let old = std::mem::replace(&mut self.current_version_reference, value); - old - } -} - -impl PBXObjectExt for XCVersionGroup { - fn from_hashmap(mut value: PBXHashMap, objects: WeakPBXObjectCollection) -> anyhow::Result - where - Self: Sized, - { - Ok(Self { - current_version_reference: value.remove_string("current_version"), - version_group_type: value.remove_string("version_group_type"), - inner: PBXObjectExt::from_hashmap(value, objects)?, - }) - } - - fn to_hashmap(&self) -> PBXHashMap { - todo!() - } -} diff --git a/src/pbxproj/object/file/mod.rs b/src/pbxproj/object/file/mod.rs index 0cf7652..17e3ace 100644 --- a/src/pbxproj/object/file/mod.rs +++ b/src/pbxproj/object/file/mod.rs @@ -1,11 +1,2 @@ mod container_item; -mod element; -mod group; -mod reference; -mod source_tree; - pub use container_item::*; -pub use element::*; -pub use group::*; -pub use reference::*; -pub use source_tree::*; diff --git a/src/pbxproj/object/file/reference.rs b/src/pbxproj/object/file/reference.rs deleted file mode 100644 index 01bace2..0000000 --- a/src/pbxproj/object/file/reference.rs +++ /dev/null @@ -1,49 +0,0 @@ -use crate::pbxproj::*; - -/// [`PBXObject`] poitning to an external file referenced by the project -/// -/// [`PBXObject`]: crate::pbxproj::PBXObject -#[derive(Debug, derive_deref_rs::Deref, derive_new::new)] -pub struct PBXFileReference { - /// Text encoding of file content - pub file_encoding: Option, - /// User-specified file type. Typically this is not set and you want to use `lastKnownFileType` instead. - pub explicit_file_type: Option, - /// Derived file type. For a file named "foo.swift" this value would be "sourcecode.swift" - pub last_known_file_type: Option, - /// Line ending type for the file - pub line_ending: Option, - /// Legacy programming language identifier - pub language_specification_identifier: Option, - /// Programming language identifier - pub xc_language_specification_identifier: Option, - /// Plist organizational family identifier - pub plist_structure_definition_identifier: Option, - #[deref] - inner: PBXFileElement, -} - -impl PBXObjectExt for PBXFileReference { - fn from_hashmap(mut value: PBXHashMap, objects: WeakPBXObjectCollection) -> anyhow::Result - where - Self: Sized, - { - Ok(Self { - file_encoding: value.remove_number("fileEncoding"), - explicit_file_type: value.remove_string("explicitFileType"), - last_known_file_type: value.remove_string("lastKnownFileType"), - line_ending: value.remove_number("lineEnding"), - language_specification_identifier: value - .remove_string("languageSpecificationIdentifier"), - xc_language_specification_identifier: value - .remove_string("xcLanguageSpecificationIdentifier"), - plist_structure_definition_identifier: value - .remove_string("xcLanguageSpecificationIdentifier"), - inner: PBXFileElement::from_hashmap(value, objects)?, - }) - } - - fn to_hashmap(&self) -> PBXHashMap { - todo!() - } -} diff --git a/src/pbxproj/object/file/source_tree.rs b/src/pbxproj/object/file/source_tree.rs deleted file mode 100644 index df4bc1b..0000000 --- a/src/pbxproj/object/file/source_tree.rs +++ /dev/null @@ -1,57 +0,0 @@ -/// Specifies source trees for files -/// Corresponds to the "Location" dropdown in Xcode's File Inspector -#[derive(PartialEq, Eq, Debug)] -pub enum PBXFileSourceTree { - /// No source tree - None, - /// Absolute source tree - Absolute, - /// Group source tree - Group, - /// Root tree - SourceRoot, - /// Products Directory source tree - BuildProductsDir, - /// SDK root source tree - SdkRoot, - /// Developer Directory source tree - DeveloperDir, - /// Custom source tree - Custom(String), -} - -impl ToString for PBXFileSourceTree { - fn to_string(&self) -> String { - match self { - PBXFileSourceTree::None => "", - PBXFileSourceTree::Absolute => "", - PBXFileSourceTree::Group => "", - PBXFileSourceTree::SourceRoot => "SOURCE_ROOT", - PBXFileSourceTree::BuildProductsDir => "BUILT_PRODUCTS_DIR", - PBXFileSourceTree::SdkRoot => "SDKROOT", - PBXFileSourceTree::DeveloperDir => "DEVELOPER_DIR", - PBXFileSourceTree::Custom(s) => s, - } - .into() - } -} -impl From for PBXFileSourceTree { - fn from(s: String) -> Self { - Self::from(s.as_str()) - } -} - -impl From<&str> for PBXFileSourceTree { - fn from(s: &str) -> Self { - match s { - "" => PBXFileSourceTree::None, - "" => PBXFileSourceTree::Absolute, - "" => PBXFileSourceTree::Group, - "SOURCE_ROOT" => PBXFileSourceTree::SourceRoot, - "BUILT_PRODUCTS_DIR" => PBXFileSourceTree::BuildProductsDir, - "SDKROOT" => PBXFileSourceTree::SdkRoot, - "DEVELOPER_DIR" => PBXFileSourceTree::DeveloperDir, - s => PBXFileSourceTree::Custom(s.into()), - } - } -} diff --git a/src/pbxproj/object/fs/kind.rs b/src/pbxproj/object/fs/kind.rs new file mode 100644 index 0000000..d9e95a9 --- /dev/null +++ b/src/pbxproj/object/fs/kind.rs @@ -0,0 +1,54 @@ +use derive_is_enum_variant::is_enum_variant; + +#[derive(Debug, PartialEq, Eq, is_enum_variant, Ord, PartialOrd)] +/// [`PBXFSKind`] group abstraction kind +pub enum PBXGroupKind { + /// PBXGroup + FileGroup, + /// XCVersionGroup + VersionGroup, + /// XCVersionGroup + VariantGroup, +} + +impl PBXGroupKind { + /// Return string representation of [`PBXGroupKind`] + pub fn as_isa(&self) -> &str { + match self { + PBXGroupKind::FileGroup => "PBXFileGroup", + PBXGroupKind::VersionGroup => "XCVersionGroup", + PBXGroupKind::VariantGroup => "PBXVariantGroup", + } + } +} + +impl Default for PBXGroupKind { + fn default() -> Self { + Self::FileGroup + } +} + +#[derive(Debug, PartialEq, Eq, is_enum_variant, Ord, PartialOrd)] +/// [`PBXFSReference`] abstraction kind +pub enum PBXFSReferenceKind { + /// Group + Group(PBXGroupKind), + /// PBXFileReference + File, +} + +impl PBXFSReferenceKind { + /// Return string representation of [`PBXFSKind`] + pub fn as_isa(&self) -> &str { + match self { + PBXFSReferenceKind::Group(group_kind) => group_kind.as_isa(), + PBXFSReferenceKind::File => "PBXFileReference", + } + } +} + +impl Default for PBXFSReferenceKind { + fn default() -> Self { + Self::Group(PBXGroupKind::default()) + } +} diff --git a/src/pbxproj/object/fs/mod.rs b/src/pbxproj/object/fs/mod.rs new file mode 100644 index 0000000..86ebd88 --- /dev/null +++ b/src/pbxproj/object/fs/mod.rs @@ -0,0 +1,136 @@ +mod kind; +mod obj; +mod setget; +mod source_tree; +use super::*; +use std::{cell::RefCell, collections::HashSet, rc::Rc}; + +pub use kind::*; +pub use source_tree::*; + +/// Abstraction over `PBXFileReference`, `PBXGroup`, `PBXVariantGroup`, and `XCVersionGroup` +#[derive(Debug, Default)] +pub struct PBXFSReference { + /// Element source tree. + source_tree: Option, + /// Element path. + path: Option, + /// Element name. + name: Option, + /// Element include in index. + include_in_index: Option, + /// Element uses tabs. + uses_tabs: Option, + /// Element indent width. + indent_width: Option, + /// Element tab width. + tab_width: Option, + /// Element wraps lines. + wraps_lines: Option, + /// Element parent. + kind: PBXFSReferenceKind, + /// Group children references (only relevant to PBX*Group) + children_references: Option>, + /// Text encoding of file content (only relevant to PBXFileReference) + file_encoding: Option, + /// User-specified file type. use `last_known_file_type` instead. (only relevant to PBXFileReference) + explicit_file_type: Option, + /// Derived file type. For a file named "foo.swift" this value would be "sourcecode.swift" (only relevant to PBXFileReference) + last_known_file_type: Option, + /// Line ending type for the file (only relevant to PBXFileReference) + line_ending: Option, + /// Legacy programming language identifier (only relevant to PBXFileReference) + language_specification_identifier: Option, + /// Programming language identifier (only relevant to PBXFileReference) + xc_language_specification_identifier: Option, + /// Plist organizational family identifier (only relevant to PBXFileReference) + plist_structure_definition_identifier: Option, + /// Current version. (only relevant for XCVersionGroup) + current_version_reference: Option, + /// Version group type. (only relevant for XCVersionGroup) + version_group_type: Option, + + pub(crate) parent_reference: Option, + pub(crate) objects: WeakPBXObjectCollection, +} + +impl PBXFSReference { + /// Get a reference to the pbxfile element's parent reference. + #[must_use] + pub fn parent(&self) -> Option>> { + self.objects + .upgrade()? + .borrow() + .get(self.parent_reference.as_ref()?)? + .as_pbxfs_reference() + .map(|r| r.clone()) + } + + /// Get Group children. + /// WARN: This will return empty if self is of type file + pub fn children(&self) -> Vec>> { + if self.is_file() || self.children_references.is_none() { + return vec![]; + } + let objects = self.objects.upgrade().expect("Objects to valid reference"); + let objects = objects.borrow(); + self.children_references + .as_ref() + .unwrap() + .iter() + .map(|r| Some(objects.get(r)?.as_pbxfs_reference()?.clone())) + .flatten() + .collect::>() + } + + /// Get group from children with given name + /// + /// NOTE: This will return None if self is file + pub fn get_subgroup(&self, name: &str) -> Option>> { + if self.is_file() { + return None; + } + + self.children() + .into_iter() + .filter(|v| v.borrow().is_group()) + .find(|v| { + let group = v.borrow(); + if let Some(group_path) = group.path() { + group_path.eq(name) + } else if let Some(group_name) = group.name() { + group_name.eq(name) + } else { + false + } + }) + } +} + +impl Eq for PBXFSReference {} +impl PartialEq for PBXFSReference { + fn eq(&self, other: &Self) -> bool { + self.kind == other.kind + && self.source_tree == other.source_tree + && self.path == other.path + && self.name == other.name + && self.parent_reference == other.parent_reference + && self.children_references == other.children_references + && self.current_version_reference == other.current_version_reference + && self.version_group_type == other.version_group_type + && self.include_in_index == other.include_in_index + && self.uses_tabs == other.uses_tabs + && self.indent_width == other.indent_width + && self.tab_width == other.tab_width + && self.wraps_lines == other.wraps_lines + && self.file_encoding == other.file_encoding + && self.explicit_file_type == other.explicit_file_type + && self.last_known_file_type == other.last_known_file_type + && self.line_ending == other.line_ending + && self.language_specification_identifier == other.language_specification_identifier + && self.xc_language_specification_identifier + == other.xc_language_specification_identifier + && self.plist_structure_definition_identifier + == other.plist_structure_definition_identifier + } +} diff --git a/src/pbxproj/object/fs/obj.rs b/src/pbxproj/object/fs/obj.rs new file mode 100644 index 0000000..e3c5cc1 --- /dev/null +++ b/src/pbxproj/object/fs/obj.rs @@ -0,0 +1,48 @@ +use crate::pbxproj::PBXHashMap; +use anyhow::Result; + +use super::*; +impl PBXObjectExt for PBXFSReference { + fn from_hashmap(mut value: PBXHashMap, objects: WeakPBXObjectCollection) -> Result + where + Self: Sized, + { + let kind = value + .try_remove_kind("isa")? + .try_into_fs_reference_kind() + .unwrap(); + Ok(Self { + name: value.remove_string("name"), + path: value.remove_string("path"), + kind, + source_tree: value.remove_string("sourceTree").map(|s| s.into()), + include_in_index: value.remove_number("includeInIndex").map(|v| v == 1), + uses_tabs: value.remove_number("usesTabs").map(|v| v == 1), + indent_width: value.remove_number("indentWidth"), + tab_width: value.remove_number("tabWidth"), + wraps_lines: value.remove_number("wrapsLines").map(|v| v == 1), + current_version_reference: value.remove_string("currentVersion"), + children_references: value + .remove_vec("children") + .map(|v| v.try_into_vec_strings().ok().map(|v| HashSet::from_iter(v))) + .flatten(), + parent_reference: None, + file_encoding: value.remove_number("fileEncoding"), + explicit_file_type: value.remove_string("explicitFileType"), + last_known_file_type: value.remove_string("lastKnownFileType"), + line_ending: value.remove_number("lineEnding"), + language_specification_identifier: value + .remove_string("languageSpecificationIdentifier"), + xc_language_specification_identifier: value + .remove_string("xcLanguageSpecificationIdentifier"), + plist_structure_definition_identifier: value + .remove_string("xcLanguageSpecificationIdentifier"), + objects, + version_group_type: value.remove_string("versioGroupType"), + }) + } + + fn to_hashmap(&self) -> PBXHashMap { + todo!() + } +} diff --git a/src/pbxproj/object/fs/setget.rs b/src/pbxproj/object/fs/setget.rs new file mode 100644 index 0000000..9bfe202 --- /dev/null +++ b/src/pbxproj/object/fs/setget.rs @@ -0,0 +1,258 @@ +use super::*; +impl PBXFSReference { + /// Check whether this fs reference is group + pub fn is_group(&self) -> bool { + self.kind.is_group() + } + + /// Check whether this fs reference is group and is file group + pub fn is_file_group(&self) -> bool { + if let PBXFSReferenceKind::Group(ref group) = self.kind { + group.is_file_group() + } else { + false + } + } + + /// Check whether this fs reference is group and is version group + pub fn is_version_group(&self) -> bool { + if let PBXFSReferenceKind::Group(ref group) = self.kind { + group.is_version_group() + } else { + false + } + } + + /// Check whether this fs reference is group and is variant group + pub fn is_varient_group(&self) -> bool { + if let PBXFSReferenceKind::Group(ref group) = self.kind { + group.is_variant_group() + } else { + false + } + } + + /// Check whether this fs reference is file + pub fn is_file(&self) -> bool { + self.kind.is_file() + } + + /// Get a reference to the reference's source tree. + #[must_use] + pub fn source_tree(&self) -> Option<&PBXSourceTree> { + self.source_tree.as_ref() + } + + /// Get a reference to the reference's path. + #[must_use] + pub fn path(&self) -> Option<&String> { + self.path.as_ref() + } + + /// Get a reference to the reference's name. + #[must_use] + pub fn name(&self) -> Option<&String> { + self.name.as_ref() + } + + /// Get the reference's include in index. + #[must_use] + pub fn include_in_index(&self) -> Option { + self.include_in_index + } + + /// Get the reference's uses tabs. + #[must_use] + pub fn uses_tabs(&self) -> Option { + self.uses_tabs + } + + /// Get the reference's indent width. + #[must_use] + pub fn indent_width(&self) -> Option { + self.indent_width + } + + /// Get the reference's tab width. + #[must_use] + pub fn tab_width(&self) -> Option { + self.tab_width + } + + /// Get the reference's wraps lines. + #[must_use] + pub fn wraps_lines(&self) -> Option { + self.wraps_lines + } + + /// Get a reference to the reference's kind. + #[must_use] + pub fn kind(&self) -> &PBXFSReferenceKind { + &self.kind + } + + /// Get a reference to the reference's children references. + /// WARN: Would panic if !self.is_group() + #[must_use] + pub fn children_references(&self) -> &HashSet { + &self.children_references.as_ref().unwrap() + } + + /// Get the reference's file encoding. + #[must_use] + pub fn file_encoding(&self) -> Option { + self.file_encoding + } + + /// Get a reference to the reference's explicit file type. + #[must_use] + pub fn explicit_file_type(&self) -> Option<&String> { + self.explicit_file_type.as_ref() + } + + /// Get a reference to the reference's last known file type. + #[must_use] + pub fn last_known_file_type(&self) -> Option<&String> { + self.last_known_file_type.as_ref() + } + + /// Get the reference's line ending. + #[must_use] + pub fn line_ending(&self) -> Option { + self.line_ending + } + + /// Get a reference to the reference's language specification identifier. + #[must_use] + pub fn language_specification_identifier(&self) -> Option<&String> { + self.language_specification_identifier.as_ref() + } + + /// Get a reference to the reference's xc language specification identifier. + #[must_use] + pub fn xc_language_specification_identifier(&self) -> Option<&String> { + self.xc_language_specification_identifier.as_ref() + } + + /// Get a reference to the reference's plist structure definition identifier. + #[must_use] + pub fn plist_structure_definition_identifier(&self) -> Option<&String> { + self.plist_structure_definition_identifier.as_ref() + } + + /// Get a reference to the reference's current version reference. + #[must_use] + pub fn current_version_reference(&self) -> Option<&String> { + self.current_version_reference.as_ref() + } + + /// Get a reference to the reference's version group type. + #[must_use] + pub fn version_group_type(&self) -> Option<&String> { + self.version_group_type.as_ref() + } + + /// Set the reference's source tree. + pub fn set_source_tree(&mut self, source_tree: PBXSourceTree) { + self.source_tree = Some(source_tree); + } + + /// Set the reference's path. + pub fn set_path(&mut self, path: Option) { + self.path = path; + } + + /// Set the reference's name. + pub fn set_name(&mut self, name: Option) { + self.name = name; + } + + /// Set the reference's include in index. + pub fn set_include_in_index(&mut self, include_in_index: Option) { + self.include_in_index = include_in_index; + } + + /// Set the reference's uses tabs. + pub fn set_uses_tabs(&mut self, uses_tabs: Option) { + self.uses_tabs = uses_tabs; + } + + /// Set the reference's indent width. + pub fn set_indent_width(&mut self, indent_width: Option) { + self.indent_width = indent_width; + } + + /// Set the reference's tab width. + pub fn set_tab_width(&mut self, tab_width: Option) { + self.tab_width = tab_width; + } + + /// Set the reference's wraps lines. + pub fn set_wraps_lines(&mut self, wraps_lines: Option) { + self.wraps_lines = wraps_lines; + } + + /// Set the reference's kind. + pub fn set_kind(&mut self, kind: PBXFSReferenceKind) { + self.kind = kind; + } + + /// Set the reference's children references. + pub fn set_children_references(&mut self, children_references: HashSet) { + self.children_references = Some(children_references); + } + + /// Set the reference's file encoding. + pub fn set_file_encoding(&mut self, file_encoding: Option) { + self.file_encoding = file_encoding; + } + + /// Set the reference's explicit file type. + pub fn set_explicit_file_type(&mut self, explicit_file_type: Option) { + self.explicit_file_type = explicit_file_type; + } + + /// Set the reference's last known file type. + pub fn set_last_known_file_type(&mut self, last_known_file_type: Option) { + self.last_known_file_type = last_known_file_type; + } + + /// Set the reference's line ending. + pub fn set_line_ending(&mut self, line_ending: Option) { + self.line_ending = line_ending; + } + + /// Set the reference's language specification identifier. + pub fn set_language_specification_identifier( + &mut self, + language_specification_identifier: Option, + ) { + self.language_specification_identifier = language_specification_identifier; + } + + /// Set the reference's xc language specification identifier. + pub fn set_xc_language_specification_identifier( + &mut self, + xc_language_specification_identifier: Option, + ) { + self.xc_language_specification_identifier = xc_language_specification_identifier; + } + + /// Set the reference's plist structure definition identifier. + pub fn set_plist_structure_definition_identifier( + &mut self, + plist_structure_definition_identifier: Option, + ) { + self.plist_structure_definition_identifier = plist_structure_definition_identifier; + } + + /// Set the reference's current version reference. + pub fn set_current_version_reference(&mut self, current_version_reference: Option) { + self.current_version_reference = current_version_reference; + } + + /// Set the reference's version group type. + pub fn set_version_group_type(&mut self, version_group_type: Option) { + self.version_group_type = version_group_type; + } +} diff --git a/src/pbxproj/object/fs/source_tree.rs b/src/pbxproj/object/fs/source_tree.rs new file mode 100644 index 0000000..9d4fe96 --- /dev/null +++ b/src/pbxproj/object/fs/source_tree.rs @@ -0,0 +1,64 @@ +/// Helper Specifying source trees for files +/// +/// Corresponds to the "Location" dropdown in Xcode's File Inspector +#[derive(PartialEq, Eq, Debug)] +pub enum PBXSourceTree { + /// No source tree + None, + /// Absolute source tree + Absolute, + /// Group source tree + Group, + /// Root tree + SourceRoot, + /// Products Directory source tree + BuildProductsDir, + /// SDK root source tree + SdkRoot, + /// Developer Directory source tree + DeveloperDir, + /// Custom source tree + Custom(String), +} + +impl Default for PBXSourceTree { + fn default() -> Self { + PBXSourceTree::None + } +} + +impl ToString for PBXSourceTree { + fn to_string(&self) -> String { + match self { + Self::None => "", + Self::Absolute => "", + Self::Group => "", + Self::SourceRoot => "SOURCE_ROOT", + Self::BuildProductsDir => "BUILT_PRODUCTS_DIR", + Self::SdkRoot => "SDKROOT", + Self::DeveloperDir => "DEVELOPER_DIR", + Self::Custom(s) => s, + } + .into() + } +} +impl From for PBXSourceTree { + fn from(s: String) -> Self { + Self::from(s.as_str()) + } +} + +impl From<&str> for PBXSourceTree { + fn from(s: &str) -> Self { + match s { + "" => Self::None, + "" => Self::Absolute, + "" => Self::Group, + "SOURCE_ROOT" => Self::SourceRoot, + "BUILT_PRODUCTS_DIR" => Self::BuildProductsDir, + "SDKROOT" => Self::SdkRoot, + "DEVELOPER_DIR" => Self::DeveloperDir, + s => Self::Custom(s.into()), + } + } +} diff --git a/src/pbxproj/object/kind.rs b/src/pbxproj/object/kind.rs index ab2b3a5..fc3617b 100644 --- a/src/pbxproj/object/kind.rs +++ b/src/pbxproj/object/kind.rs @@ -1,7 +1,7 @@ use derive_is_enum_variant::is_enum_variant; use enum_as_inner::EnumAsInner; -use super::PBXBuildPhaseKind; +use super::{PBXBuildPhaseKind, PBXFSReferenceKind, PBXGroupKind}; /// Representation of all Target kinds #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, is_enum_variant)] @@ -17,12 +17,12 @@ pub enum PBXTargetKind { #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, EnumAsInner)] /// Pbxproj object kinds pub enum PBXObjectKind { - /// A Kind representing: + /// An abstraction over targets, including: /// - PBXAggregateTarget: A build target that aggregates several others. /// - PBXLegacyTarget: A build target that according to Xcode is an "External Build System". /// - PBXNativeTarget: A build target that produces a binary content (application or library). PBXTarget(PBXTargetKind), - /// A Kind representing: + /// An abstraction over build phases, including: /// - PBXCopyFilesBuildPhase: Copy file build phase. /// - PBXFrameworksBuildPhase: Framework link build phase. /// - PBXHeadersBuildPhase: Headers link build phase. @@ -31,6 +31,14 @@ pub enum PBXObjectKind { /// - PBXShellScriptBuildPhase: Shell Script build phase. /// - PBXSourcesBuildPhase: A Kind representing the sources compilation build phase. PBXBuildPhase(PBXBuildPhaseKind), + /// An abstraction over PBXFileReference and PBX*Group: + /// - PBXFileReference: track every external file referenced by the project: source files, + /// resource files, libraries, generated application files, and so on. + /// - PBXGroup: Files group + /// - XCVersionGroup: Group that contains multiple files references to the different versions + /// of a resource. Used to contain the different versions of a xcdatamodel + /// - PBXVariantGroup: a reference localized resources. + PBXFSReference(PBXFSReferenceKind), /// A Kind for defining build configurations XCBuildConfiguration, /// A Kind indicating a file reference that is used in a BuildPhase (either as an include or resource). @@ -41,11 +49,6 @@ pub enum PBXObjectKind { XCConfigurationList, /// A Kind representing Decoration for a target element PBXContainerItemProxy, - /// A Kind representing to track every external file referenced by the project: source files, - /// resource files, libraries, generated application files, and so on. - PBXFileReference, - /// A Kind representing group files - PBXGroup, /// A Kind representing a build target that produces a binary content (application or library). PBXProject, /// A Kind representing an abstract parent for specialized targets. @@ -54,11 +57,6 @@ pub enum PBXObjectKind { XCSwiftPackageProductDependency, /// A Kind representing a reference to other targets through content proxies. PBXTargetDependency, - /// a Kind representing a reference localized resources. - PBXVariantGroup, - /// Kind representing Group that contains multiple files references to the different versions - /// of a resource. Used to contain the different versions of a xcdatamodel - XCVersionGroup, /// UnknownPBXObjectKind Unknown(String), } @@ -81,18 +79,27 @@ impl PBXObjectKind { Err(self) } } + + /// Try get inner PBXFSReference + pub fn try_into_fs_reference_kind(self) -> Result { + if let Self::PBXFSReference(v) = self { + Ok(v) + } else { + Err(self) + } + } } impl From<&str> for PBXObjectKind { fn from(s: &str) -> Self { match s { "PBXBuildFile" => Self::PBXBuildFile, - "PBXFileReference" => Self::PBXFileReference, + "PBXFileReference" => Self::PBXFSReference(PBXFSReferenceKind::File), "PBXLegacyTarget" => Self::PBXTarget(PBXTargetKind::Legacy), "PBXNativeTarget" => Self::PBXTarget(PBXTargetKind::Native), "PBXAggregateTarget" => Self::PBXTarget(PBXTargetKind::Aggregate), "PBXProject" => Self::PBXProject, - "PBXGroup" => Self::PBXGroup, + "PBXGroup" => Self::PBXFSReference(PBXFSReferenceKind::Group(PBXGroupKind::FileGroup)), "PBXHeadersBuildPhase" => Self::PBXBuildPhase(PBXBuildPhaseKind::Headers), "PBXFrameworksBuildPhase" => Self::PBXBuildPhase(PBXBuildPhaseKind::Frameworks), "PBXResourcesBuildPhase" => Self::PBXBuildPhase(PBXBuildPhaseKind::Resources), @@ -102,10 +109,14 @@ impl From<&str> for PBXObjectKind { "PBXRezBuildPhase" => Self::PBXBuildPhase(PBXBuildPhaseKind::CarbonResources), "XCConfigurationList" => Self::XCConfigurationList, "PBXTargetDependency" => Self::PBXTargetDependency, - "PBXVariantGroup" => Self::PBXVariantGroup, + "PBXVariantGroup" => { + Self::PBXFSReference(PBXFSReferenceKind::Group(PBXGroupKind::VariantGroup)) + } "XCBuildConfiguration" => Self::XCBuildConfiguration, "PBXContainerItemProxy" => Self::PBXContainerItemProxy, - "XCVersionGroup" => Self::XCVersionGroup, + "XCVersionGroup" => { + Self::PBXFSReference(PBXFSReferenceKind::Group(PBXGroupKind::VersionGroup)) + } "PBXBuildRule" => Self::PBXBuildRule, "XCRemoteSwiftPackageReference" => Self::XCRemoteSwiftPackageReference, "XCSwiftPackageProductDependency" => Self::XCSwiftPackageProductDependency, @@ -118,18 +129,15 @@ impl ToString for PBXObjectKind { fn to_string(&self) -> String { match self { Self::PBXBuildFile => "PBXBuildFile", - Self::PBXFileReference => "PBXFileReference", Self::PBXProject => "PBXProject", - Self::PBXGroup => "PBXGroup", Self::XCConfigurationList => "XCConfigurationList", Self::PBXTargetDependency => "PBXTargetDependency", - Self::PBXVariantGroup => "PBXVariantGroup", Self::XCBuildConfiguration => "XCBuildConfiguration", Self::PBXContainerItemProxy => "PBXContainerItemProxy", - Self::XCVersionGroup => "XCVersionGroup", Self::PBXBuildRule => "PBXBuildRule", Self::XCRemoteSwiftPackageReference => "XCRemoteSwiftPackageReference", Self::XCSwiftPackageProductDependency => "XCSwiftPackageProductDependency", + Self::PBXFSReference(kind) => kind.as_isa(), Self::PBXTarget(kind) => match kind { PBXTargetKind::Native => "PBXNativeTarget", PBXTargetKind::Legacy => "PBXLegacyTarget", diff --git a/src/pbxproj/object/meta.rs b/src/pbxproj/object/meta.rs index aa7a306..1904adc 100644 --- a/src/pbxproj/object/meta.rs +++ b/src/pbxproj/object/meta.rs @@ -14,6 +14,8 @@ pub enum PBXObject { PBXTarget(Rc>), /// Abstraction over Build phases PBXBuildPhase(Rc>), + /// Abstraction over PBXFileReference and PBX*Group + PBXFSReference(Rc>), /// A Kind for defining build configurations XCBuildConfiguration(Rc>), /// File referenced by a build phase, unique to each build phase. @@ -24,11 +26,6 @@ pub enum PBXObject { XCConfigurationList(Rc>), /// A Kind representing Decoration for a target element PBXContainerItemProxy(Rc>), - /// A Kind representing to track every external file referenced by the project: source files, - /// resource files, libraries, generated application files, and so on. - PBXFileReference(Rc>), - /// A Kind representing group files - PBXGroup(Rc>), /// A Kind representing a build target that produces a binary content (application or library). PBXProject(Rc>), /// A Kind representing an abstract parent for specialized targets. @@ -37,11 +34,6 @@ pub enum PBXObject { XCSwiftPackageProductDependency(Rc>), /// A Kind representing a reference to other targets through content proxies. PBXTargetDependency(Rc>), - /// a Kind representing a reference localized resources. - PBXVariantGroup(Rc>), - /// Kind representing Group that contains multiple files references to the different versions - /// of a resource. Used to contain the different versions of a xcdatamodel - XCVersionGroup(Rc>), } impl PBXObject { @@ -68,8 +60,6 @@ impl PBXObject { PBXObjectKind::PBXBuildRule => into!(PBXBuildRule, map, objects), PBXObjectKind::XCConfigurationList => into!(XCConfigurationList, map, objects), PBXObjectKind::PBXContainerItemProxy => into!(PBXContainerItemProxy, map, objects), - PBXObjectKind::PBXFileReference => into!(PBXFileReference, map, objects), - PBXObjectKind::PBXGroup => into!(PBXGroup, map, objects), PBXObjectKind::PBXProject => into!(PBXProject, map, objects), PBXObjectKind::XCRemoteSwiftPackageReference => { into!(XCRemoteSwiftPackageReference, map, objects) @@ -78,8 +68,7 @@ impl PBXObject { into!(XCSwiftPackageProductDependency, map, objects) } PBXObjectKind::PBXTargetDependency => into!(PBXTargetDependency, map, objects), - PBXObjectKind::PBXVariantGroup => into!(PBXVariantGroup, map, objects), - PBXObjectKind::XCVersionGroup => into!(XCVersionGroup, map, objects), + PBXObjectKind::PBXFSReference(_) => into!(PBXFSReference, map, objects), PBXObjectKind::PBXTarget(_) => into!(PBXTarget, map, objects), PBXObjectKind::PBXBuildPhase(_) => into!(PBXBuildPhase, map, objects), diff --git a/src/pbxproj/object/mod.rs b/src/pbxproj/object/mod.rs index 0010c05..2c0d596 100644 --- a/src/pbxproj/object/mod.rs +++ b/src/pbxproj/object/mod.rs @@ -6,9 +6,11 @@ mod swift_package; mod target; mod collection; +mod fs; mod kind; mod product_type; +pub use fs::*; pub use kind::*; pub use product_type::*; diff --git a/src/pbxproj/object/project.rs b/src/pbxproj/object/project.rs index fd5f08a..e8d507a 100644 --- a/src/pbxproj/object/project.rs +++ b/src/pbxproj/object/project.rs @@ -123,28 +123,34 @@ impl PBXProject { } /// Get Project main group. - pub fn main_group(&self) -> Rc> { + pub fn main_group(&self) -> Rc> { self.objects .upgrade() .expect("objects weak is invalid") .borrow() .get(&self.main_group_reference) .expect("PBXProject should contain mainGroup") - .as_pbx_group() + .as_pbxfs_reference() .expect("given reference point to PBXGroup") .clone() } /// Products Group - pub fn products_group(&self) -> Option>> { + pub fn products_group(&self) -> Option>> { let products_group = self.products_group_reference.as_ref()?; - self.objects + let fs_reference = self + .objects .upgrade()? .borrow() .get(products_group)? - .as_pbx_group()? - .clone() - .pipe(Some) + .as_pbxfs_reference()? + .clone(); + + if fs_reference.borrow().is_group() { + Some(fs_reference) + } else { + None + } } /// Get attributes for a given target reference