Skip to content

Commit

Permalink
ref: isolate and rename root object PBXRootObject
Browse files Browse the repository at this point in the history
  • Loading branch information
kkharji committed Jun 7, 2022
1 parent a4c4b26 commit 697c230
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 70 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "xcodeproj"
version = "0.1.3"
version = "0.1.4"
edition = "2021"
description = "xcodeproj reader and parser."
license = "MIT OR Apache-2.0"
Expand Down
78 changes: 26 additions & 52 deletions src/pbxproj/pest/mod.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,46 @@
#![allow(missing_docs)]
use super::object::PBXObjectKind;
use super::{PBXArray, PBXHashMap, PBXProjectData};
use super::{PBXArray, PBXHashMap};
use crate::pbxproj::PBXValue;
use anyhow::Context;
use anyhow::{anyhow, Context, Result};
use convert_case::{Case, Casing};
use itertools::Itertools;
use std::path::{Path, PathBuf};
use std::path::Path;
use std::{collections::HashMap, num::ParseIntError};

use pest_consume::*;
use tap::Pipe;

/// Pest Parser to parse into [`XProj`]
/// Pest Parser
#[derive(Parser)]
#[grammar = "pbxproj/pest/grammar.pest"]
pub(crate) struct PBXProjectParser;

pub(crate) type NodeResult<T> = std::result::Result<T, Error<Rule>>;
pub(crate) type Node<'i> = pest_consume::Node<'i, Rule, ()>;

impl PBXProjectParser {
pub fn try_parse_from_file<P>(path: P) -> Result<PBXHashMap>
where
P: AsRef<Path> + std::fmt::Debug,
{
std::fs::read_to_string(&path)
.map_err(|e| anyhow!("PBXProjectData from path {path:?}: {e}"))?
.pipe(Self::try_from_str)
}

pub fn try_from_str<S>(content: S) -> Result<PBXHashMap>
where
S: AsRef<str>,
{
PBXProjectParser::parse(Rule::file, content.as_ref())
.context("Parse content")?
.pipe(|n| n.single().context("nodes to single node"))?
.pipe(PBXProjectParser::file)
.context("parse into PBXHashMap")
}
}

#[parser]
impl PBXProjectParser {
fn key(input: Node) -> NodeResult<String> {
Expand Down Expand Up @@ -120,54 +142,6 @@ impl PBXProjectParser {
}
}

impl TryFrom<&str> for PBXProjectData {
type Error = anyhow::Error;
fn try_from(content: &str) -> anyhow::Result<Self> {
let nodes = PBXProjectParser::parse(Rule::file, content).context("Parse content")?;
let node = nodes.single().context("nodes to single")?;
let mut object = PBXProjectParser::file(node)?;

let archive_version = object.try_remove_number("archive_version")? as u8;
let object_version = object.try_remove_number("object_version")? as u8;
let classes = object.try_remove_object("classes").unwrap_or_default();
let root_object_reference = object.try_remove_string("root_object")?;

let objects = object.try_remove_object("objects")?;
Ok(Self::new(
archive_version,
object_version,
classes,
objects,
root_object_reference,
))
}
}

impl TryFrom<String> for PBXProjectData {
type Error = anyhow::Error;
fn try_from(content: String) -> anyhow::Result<Self> {
PBXProjectData::try_from(content.as_str())
}
}

impl TryFrom<&Path> for PBXProjectData {
type Error = anyhow::Error;

fn try_from(value: &Path) -> anyhow::Result<Self> {
std::fs::read_to_string(&value)
.map_err(|e| anyhow::anyhow!("PBXProjectData from path {value:?}: {e}"))?
.pipe(TryFrom::try_from)
}
}

impl TryFrom<PathBuf> for PBXProjectData {
type Error = anyhow::Error;

fn try_from(value: PathBuf) -> anyhow::Result<Self> {
Self::try_from(value.as_path())
}
}

#[cfg(test)]
macro_rules! test_file {
($path:expr) => {{
Expand Down
71 changes: 54 additions & 17 deletions src/pbxproj/rep.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use super::PBXHashMap;
use anyhow::Result;
use std::path::{Path, PathBuf};
use tap::Pipe;

/// Result of Parsing *.pbxproj
#[derive(Debug)]
pub struct PBXProjectData {
#[derive(Debug, derive_new::new)]
pub struct PBXRootObject {
/// archiveVersion
archive_version: u8,
/// objectVersion
Expand All @@ -15,7 +18,7 @@ pub struct PBXProjectData {
root_object_reference: String,
}

impl PBXProjectData {
impl PBXRootObject {
/// Get the pbxproject's archive version.
#[must_use]
pub fn archive_version(&self) -> u8 {
Expand All @@ -39,45 +42,79 @@ impl PBXProjectData {
pub fn root_object_reference(&self) -> &str {
self.root_object_reference.as_ref()
}
}

impl TryFrom<PBXHashMap> for PBXRootObject {
type Error = anyhow::Error;
fn try_from(mut map: PBXHashMap) -> Result<Self> {
let archive_version = map.try_remove_number("archive_version")? as u8;
let object_version = map.try_remove_number("object_version")? as u8;
let classes = map.try_remove_object("classes").unwrap_or_default();
let root_object_reference = map.try_remove_string("root_object")?;
let objects = map.try_remove_object("objects")?;

/// Create new PBXProject with required fields
/// Use PBXProject::try_from(String/&str/Path/PathBuf) instead to parse and create object
pub fn new(
archive_version: u8,
object_version: u8,
classes: PBXHashMap,
objects: PBXHashMap,
root_object_reference: String,
) -> Self {
Self {
Ok(Self {
archive_version,
object_version,
classes,
objects,
root_object_reference,
}
})
}
}

impl TryFrom<&str> for PBXRootObject {
type Error = anyhow::Error;
fn try_from(content: &str) -> Result<Self> {
use crate::pbxproj::pest::PBXProjectParser;

PBXProjectParser::try_from_str(content)?.pipe(Self::try_from)
}
}

impl TryFrom<String> for PBXRootObject {
type Error = anyhow::Error;
fn try_from(content: String) -> Result<Self> {
Self::try_from(content.as_str())
}
}

impl TryFrom<&Path> for PBXRootObject {
type Error = anyhow::Error;

fn try_from(value: &Path) -> Result<Self> {
std::fs::read_to_string(&value)
.map_err(|e| anyhow::anyhow!("PBXProjectData from path {value:?}: {e}"))?
.pipe(TryFrom::try_from)
}
}

impl TryFrom<PathBuf> for PBXRootObject {
type Error = anyhow::Error;

fn try_from(value: PathBuf) -> Result<Self> {
Self::try_from(value.as_path())
}
}
#[test]
#[ignore = "check_output"]
fn test_parse() {
let test_content = include_str!("../../tests/samples/demo1.pbxproj");
let project = PBXProjectData::try_from(test_content).unwrap();
let project = PBXRootObject::try_from(test_content).unwrap();
println!("{project:#?}");
}

#[test]
fn test_extract_string() {
let test_content = include_str!("../../tests/samples/demo1.pbxproj");
let project = PBXProjectData::try_from(test_content).unwrap();
let project = PBXRootObject::try_from(test_content).unwrap();
// let development_region = project.extract_string("development_region");
// assert_eq!(Some(&String::from("en")), development_region);
}
#[test]
fn test_extract_value() {
let test_content = include_str!("../../tests/samples/demo2.pbxproj");
let project = PBXProjectData::try_from(test_content).unwrap();
let project = PBXRootObject::try_from(test_content).unwrap();
// let has_scanned_for_encodings = project.extract_value("has_scanned_for_encodings");
// let targets = project.extract_value("targets");
// assert_eq!(Some(&PBXValue::Number(0)), has_scanned_for_encodings);
Expand Down

0 comments on commit 697c230

Please sign in to comment.