Skip to content

Commit e5c7eb4

Browse files
authored
Merge pull request #23 from zummenix/#19-remove-by-bundle-id
Remove profiles by bundle id
2 parents e74cd3c + 4d7ed84 commit e5c7eb4

File tree

9 files changed

+98
-61
lines changed

9 files changed

+98
-61
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mprovision"
3-
version = "2.2.0"
3+
version = "3.0.0"
44
authors = ["Aleksey Kuznetsov <zummenix@gmail.com>"]
55

66
[[bin]]

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ It's very simple: `mprovision list`
4141

4242
1. The `list` subcommand accepts an optional argument `-t` or `--text` that allows you to filter the list of
4343
provisioning profiles by some text.
44-
2. The `remove` subcommand allows you to remove one or more profiles by their uuids.
44+
2. The `remove` subcommand allows you to remove one or more profiles by their uuids or bundle ids.
4545

4646
> WARNING: the `remove` subcommand is relatively dangerous since it removes profiles from the
4747
system completely.

src/bin/cli.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ pub struct ShowFileParams {
6262
#[derive(Debug, Default, PartialEq, StructOpt)]
6363
pub struct RemoveParams {
6464
#[structopt(raw(empty_values = "false"))]
65-
/// uuid(s) of provisioning profiles
66-
pub uuids: Vec<String>,
65+
/// uuid(s) or bundle id(s) of provisioning profiles
66+
pub ids: Vec<String>,
6767
#[structopt(long = "source", parse(from_os_str), raw(empty_values = "false"))]
6868
/// A directory where to search provisioning profiles
6969
pub directory: Option<PathBuf>,
@@ -287,17 +287,17 @@ mod tests {
287287
}
288288

289289
#[test]
290-
fn remove_uuid_command() {
290+
fn remove_id_command() {
291291
expect!(parse(&["mprovision", "remove", "abcd"])).to(be_ok().value(Command::Remove(
292292
RemoveParams {
293-
uuids: vec!["abcd".to_string()],
293+
ids: vec!["abcd".to_string()],
294294
directory: None,
295295
},
296296
)));
297297

298298
expect!(parse(&["mprovision", "remove", "abcd", "ef"])).to(be_ok().value(Command::Remove(
299299
RemoveParams {
300-
uuids: vec!["abcd".to_string(), "ef".to_string()],
300+
ids: vec!["abcd".to_string(), "ef".to_string()],
301301
directory: None,
302302
},
303303
)));
@@ -306,7 +306,7 @@ mod tests {
306306

307307
expect!(parse(&["mprovision", "remove", "abcd", "--source", "."])).to(be_ok().value(
308308
Command::Remove(RemoveParams {
309-
uuids: vec!["abcd".to_string()],
309+
ids: vec!["abcd".to_string()],
310310
directory: Some(".".into()),
311311
}),
312312
));
@@ -319,7 +319,7 @@ mod tests {
319319
"--source",
320320
".",
321321
])).to(be_ok().value(Command::Remove(RemoveParams {
322-
uuids: vec!["abcd".to_string(), "ef".to_string()],
322+
ids: vec!["abcd".to_string(), "ef".to_string()],
323323
directory: Some(".".into()),
324324
})));
325325

src/bin/main.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ extern crate mprovision;
77
#[macro_use]
88
extern crate structopt;
99

10-
use mprovision as mp;
10+
use chrono::*;
1111
use cli::Command;
12+
use mprovision as mp;
1213
use std::env;
1314
use std::path::PathBuf;
14-
use chrono::*;
1515

1616
mod cli;
1717

@@ -30,7 +30,7 @@ fn run(command: cli::Command) -> Result<(), cli::Error> {
3030
}) => list(text, expire_in_days, directory),
3131
Command::ShowUuid(cli::ShowUuidParams { uuid, directory }) => show_uuid(uuid, directory),
3232
Command::ShowFile(cli::ShowFileParams { file }) => show_file(file),
33-
Command::Remove(cli::RemoveParams { uuids, directory }) => remove(uuids, directory),
33+
Command::Remove(cli::RemoveParams { ids, directory }) => remove(ids, directory),
3434
Command::Clean(cli::CleanParams { directory }) => clean(directory),
3535
}
3636
}
@@ -49,21 +49,21 @@ fn list(
4949
let filter_string = text.as_ref();
5050
let mut profiles = mp::filter(entries, |profile| match (date, filter_string) {
5151
(Some(date), Some(string)) => {
52-
profile.expiration_date <= date && profile.contains(string)
52+
profile.info.expiration_date <= date && profile.info.contains(string)
5353
}
54-
(Some(date), _) => profile.expiration_date <= date,
55-
(_, Some(string)) => profile.contains(string),
54+
(Some(date), _) => profile.info.expiration_date <= date,
55+
(_, Some(string)) => profile.info.contains(string),
5656
(_, _) => true,
5757
});
58-
profiles.sort_by(|a, b| a.creation_date.cmp(&b.creation_date));
58+
profiles.sort_by(|a, b| a.info.creation_date.cmp(&b.info.creation_date));
5959
(total, profiles)
6060
})
6161
.and_then(|(total, profiles)| {
6262
if profiles.is_empty() {
6363
Ok(println!("Nothing found"))
6464
} else {
6565
for profile in &profiles {
66-
println!("\n{}", profile.description());
66+
println!("\n{}", profile.info.description());
6767
}
6868
Ok(println!("\nFound {} of {}", profiles.len(), total))
6969
}
@@ -85,14 +85,14 @@ fn show_file(path: PathBuf) -> Result<(), cli::Error> {
8585
.map_err(|err| err.into())
8686
}
8787

88-
fn remove(uuids: Vec<String>, directory: Option<PathBuf>) -> Result<(), cli::Error> {
88+
fn remove(ids: Vec<String>, directory: Option<PathBuf>) -> Result<(), cli::Error> {
8989
mp::with_directory(directory)
9090
.and_then(|directory| {
91-
mp::find_by_uuids(&directory, uuids).and_then(|profiles| {
91+
mp::find_by_ids(&directory, ids).and_then(|profiles| {
9292
for profile in profiles {
9393
match mp::remove(&profile.path) {
94-
Ok(_) => println!("'{}' was removed", profile.uuid),
95-
Err(_) => println!("Error while removing '{}'", profile.uuid),
94+
Ok(_) => println!("\nRemoved: {}", profile.info.description()),
95+
Err(_) => println!("\nError while removing '{}'", profile.info.uuid),
9696
}
9797
}
9898
Ok(())
@@ -112,7 +112,7 @@ fn clean(directory: Option<PathBuf>) -> Result<(), cli::Error> {
112112
.map_err(|err| err.into())
113113
.map(|entries| {
114114
let date = Utc::now();
115-
mp::filter(entries, |profile| profile.expiration_date <= date)
115+
mp::filter(entries, |profile| profile.info.expiration_date <= date)
116116
})
117117
.and_then(|profiles| {
118118
if profiles.is_empty() {
@@ -122,8 +122,8 @@ fn clean(directory: Option<PathBuf>) -> Result<(), cli::Error> {
122122
.iter()
123123
.map(|profile| {
124124
std::fs::remove_file(&profile.path)
125-
.map(|_| format!("'{}' was removed\n", profile.uuid))
126-
.map_err(|err| format!("'{}' {}\n", profile.uuid, err))
125+
.map(|_| format!("'{}' was removed\n", profile.info.uuid))
126+
.map_err(|err| format!("'{}' {}\n", profile.info.uuid, err))
127127
})
128128
.fold(Ok(String::new()), |acc, s| match (acc, s) {
129129
(Ok(acc), Ok(s)) => Ok(concat(acc, s)),

src/error.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use std::io;
2-
use std::fmt;
31
use std::error;
2+
use std::fmt;
3+
use std::io;
44
use std::string::FromUtf8Error;
55

66
/// An Error type.

src/lib.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,17 @@ use futures::stream::Stream;
1919
use futures::Future;
2020
use futures_cpupool::CpuPool;
2121

22-
use std::fs::{self, DirEntry, File};
23-
use std::path::{Path, PathBuf};
2422
use std::env;
23+
use std::fs::{self, DirEntry, File};
2524
use std::io::Read;
25+
use std::path::{Path, PathBuf};
2626

2727
pub use error::Error;
2828
pub use profile::Profile;
2929

3030
mod error;
31-
mod profile;
3231
mod plist_extractor;
32+
mod profile;
3333

3434
/// A Result type for this crate.
3535
pub type Result<T> = std::result::Result<T, Error>;
@@ -134,29 +134,32 @@ where
134134
/// Searches a provisioning profile by its uuid.
135135
pub fn find_by_uuid(dir: &Path, uuid: &str) -> Result<Profile> {
136136
let entries: Vec<DirEntry> = entries(dir)?.collect();
137-
if let Some(profile) = filter(entries, |profile| profile.uuid == uuid).pop() {
137+
if let Some(profile) = filter(entries, |profile| profile.info.uuid == uuid).pop() {
138138
Ok(profile)
139139
} else {
140140
Err(Error::Own(format!("Profile '{}' is not found.", uuid)))
141141
}
142142
}
143143

144-
/// Searches provisioning profiles by their uuid.
145-
pub fn find_by_uuids(dir: &Path, uuids: Vec<String>) -> Result<Vec<Profile>> {
144+
/// Searches provisioning profiles by their uuid(s) or bundle id(s).
145+
pub fn find_by_ids(dir: &Path, ids: Vec<String>) -> Result<Vec<Profile>> {
146146
let entries: Vec<DirEntry> = entries(dir)?.collect();
147-
let profiles = filter(entries, |profile| uuids.contains(&profile.uuid));
147+
let profiles = filter(entries, |profile| {
148+
ids.iter()
149+
.any(|id| id == &profile.info.uuid || profile.info.bundle_id() == Some(&id))
150+
});
148151
Ok(profiles)
149152
}
150153

151154
#[cfg(test)]
152155
mod tests {
153-
use expectest::prelude::*;
154156
use super::*;
157+
use expectest::prelude::*;
155158

156159
#[test]
157160
fn filter_mobileprovision_files() {
158-
use tempdir::TempDir;
159161
use std::fs::File;
162+
use tempdir::TempDir;
160163

161164
let temp_dir = TempDir::new("test").unwrap();
162165
let result = entries(temp_dir.path()).map(|iter| iter.count());

src/plist_extractor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ pub fn find(data: &[u8]) -> Option<&[u8]> {
2626

2727
#[cfg(test)]
2828
mod tests {
29-
use expectest::prelude::*;
3029
use super::*;
30+
use expectest::prelude::*;
3131

3232
#[test]
3333
fn test_find_plist() {

src/profile.rs

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,47 @@
1-
use std::io::{self, Read};
2-
use std::fs::File;
3-
use std::path::{Path, PathBuf};
41
use chrono::{DateTime, TimeZone, Utc};
5-
use plist::PlistEvent::*;
62
use plist;
3+
use plist::PlistEvent::*;
4+
use std::fs::File;
5+
use std::io::{self, Read};
6+
use std::path::{Path, PathBuf};
77
use {Error, Result};
88

9-
/// Represents provisioning profile data.
9+
/// Represents a file with a provisioning profile info.
1010
#[derive(Debug, Clone)]
1111
pub struct Profile {
1212
pub path: PathBuf,
13-
pub uuid: String,
14-
pub name: String,
15-
pub app_identifier: String,
16-
pub creation_date: DateTime<Utc>,
17-
pub expiration_date: DateTime<Utc>,
13+
pub info: ProfileInfo,
1814
}
1915

2016
impl Profile {
2117
/// Returns instance of the `Profile` parsed from a file.
2218
pub fn from_file(path: &Path) -> Result<Self> {
2319
let mut buf = Vec::new();
2420
File::open(path)?.read_to_end(&mut buf)?;
25-
Profile::from_xml_data(&buf)
26-
.map(|mut p| {
27-
p.path = path.to_owned();
28-
p
29-
})
30-
.ok_or_else(|| Error::Own("Couldn't parse file.".into()))
21+
let info = ProfileInfo::from_xml_data(&buf)
22+
.ok_or_else(|| Error::Own("Couldn't parse file.".into()))?;
23+
Ok(Profile {
24+
path: path.to_owned(),
25+
info,
26+
})
3127
}
28+
}
3229

30+
/// Represents provisioning profile info.
31+
#[derive(Debug, Clone)]
32+
pub struct ProfileInfo {
33+
pub uuid: String,
34+
pub name: String,
35+
pub app_identifier: String,
36+
pub creation_date: DateTime<Utc>,
37+
pub expiration_date: DateTime<Utc>,
38+
}
39+
40+
impl ProfileInfo {
3341
/// Returns instance of the `Profile` parsed from a `data`.
3442
pub fn from_xml_data(data: &[u8]) -> Option<Self> {
3543
if let Some(data) = ::plist_extractor::find(data) {
36-
let mut profile = Profile::empty();
44+
let mut profile = ProfileInfo::empty();
3745
let mut iter = plist::xml::EventReader::new(io::Cursor::new(data)).into_iter();
3846
while let Some(item) = iter.next() {
3947
if let Ok(StringValue(key)) = item {
@@ -70,9 +78,9 @@ impl Profile {
7078
}
7179
}
7280

81+
/// Returns an empty profile info.
7382
pub fn empty() -> Self {
74-
Profile {
75-
path: PathBuf::new(),
83+
ProfileInfo {
7684
uuid: "".into(),
7785
name: "".into(),
7886
app_identifier: "".into(),
@@ -93,6 +101,13 @@ impl Profile {
93101
false
94102
}
95103

104+
/// Returns a bundle id of a profile.
105+
pub fn bundle_id(&self) -> Option<&str> {
106+
self.app_identifier
107+
.find(|ch| ch == '.')
108+
.map(|i| &self.app_identifier[(i + 1)..])
109+
}
110+
96111
/// Returns profile in a text form.
97112
pub fn description(&self) -> String {
98113
let mut desc = String::new();
@@ -112,15 +127,13 @@ impl Profile {
112127

113128
#[cfg(test)]
114129
mod tests {
115-
use expectest::prelude::*;
116-
use std::path::PathBuf;
117-
use chrono::{TimeZone, Utc};
118130
use super::*;
131+
use chrono::{TimeZone, Utc};
132+
use expectest::prelude::*;
119133

120134
#[test]
121135
fn contains() {
122-
let profile = Profile {
123-
path: PathBuf::new(),
136+
let profile = ProfileInfo {
124137
uuid: "123".into(),
125138
name: "name".into(),
126139
app_identifier: "id".into(),
@@ -131,4 +144,25 @@ mod tests {
131144
expect!(profile.contains("me")).to(be_true());
132145
expect!(profile.contains("id")).to(be_true());
133146
}
147+
148+
#[test]
149+
fn correct_bundle_id() {
150+
let mut profile = ProfileInfo::empty();
151+
profile.app_identifier = "12345ABCDE.com.exmaple.app".to_owned();
152+
expect!(profile.bundle_id()).to(be_some().value("com.exmaple.app"));
153+
}
154+
155+
#[test]
156+
fn incorrect_bundle_id() {
157+
let mut profile = ProfileInfo::empty();
158+
profile.app_identifier = "12345ABCDE".to_owned();
159+
expect!(profile.bundle_id()).to(be_none());
160+
}
161+
162+
#[test]
163+
fn wildcard_bundle_id() {
164+
let mut profile = ProfileInfo::empty();
165+
profile.app_identifier = "12345ABCDE.*".to_owned();
166+
expect!(profile.bundle_id()).to(be_some().value("*"));
167+
}
134168
}

0 commit comments

Comments
 (0)