updater_lp/lib.rs
1//! # updater-lp
2//!
3//! A no frills updater that uses Github has its base.
4//! Easily create auto-updating for your cli apps!
5//!
6//! ## How it Works?
7//!
8//! Host your code on Github! Tag your code with version releases and then
9//! attach builds to those releases.
10//!
11//! _updater-lp_ then looks for the release at your repository and then version
12//! matches those release against what you state your current app version is. If
13//! it finds a newer version, it can be used to 'update' your app by replacing
14//! your current binary with the new binary from Github.
15//!
16//! ## How to use it.
17//!
18//! First off, a [test app](https://github.com/snsvrno/updater-lp-rs/tree/master/src-test)
19//! is in the source repository that shows you how its used, as well as is used to test
20//! the libraries functionality.
21//!
22//! ### Include the library
23//!
24//! Add _updater-lp_ to your `cargo.toml`.
25//!
26//! ```toml
27//! [dependencies]
28//! updater-lp = "0.2"
29//! ```
30//!
31//! ### Using the Library
32//!
33//! Then you need to set your upstream address. _updater-lp_ is struct-less (or better called static)
34//! so its recommended you have a `&'STATIC str` with your repository address.
35//!
36//! ``` rust
37//! static REPO_PATH : &str = "https://github.com/snsvrno/lpsettings-rs";
38//! ```
39//!
40//! And then where ever it makes sense, you should check for the latest version. I'd recommend putting
41//! someting to prevent checking everytime the program runs (so you don't perform too many needless github
42//! api calls) and instead check for updates daily.
43//!
44//! ```rust
45//! # extern crate updater_lp;
46//! # static REPO_PATH : &str = "https://github.com/snsvrno/lpsettings-rs";
47//! let this_version = updater_lp::create_version(&[01,2,3,4]);
48//!
49//! match updater_lp::get_latest_version(REPO_PATH) {
50//! Err(error) => {
51//! // can't check the version for some reason, `error` should state why.
52//! },
53//! Ok(latest) => {
54//! if latest > this_version {
55//! updater_lp::update_with_version(REPO_PATH,&latest);
56//! }
57//! }
58//! }
59//! ```
60//!
61//! I'd recommend against using `?` on `get_latest_version` because you probably don't want your program
62//! error / fail if you can't connect to the repo.
63//!
64//! ### A note of Versions
65//!
66//! _updater-lp_ uses [version-lp](https://crates.io/crates/version-lp) for all versions. This means
67//! that versions are easily compairable. Version is exposed in this crate to allow for easy use
68//! without requiring you to add an additional dependency to your `cargo.toml`
69
70#[macro_use] extern crate log;
71#[macro_use] extern crate failure; use failure::Error;
72#[macro_use] extern crate serde_derive;
73extern crate regex;
74extern crate restson;
75
76extern crate download_lp as download;
77extern crate archive_lp as archive;
78extern crate platform_lp as platform;
79extern crate version_lp as version; use version::Version;
80
81use std::fs;
82use std::env;
83
84mod sources;
85mod source;
86mod traits; use traits::provider::Provider;
87
88pub fn get_latest_version(repo_link : &str) -> Result<Version,Error> {
89 //! Checks the given Repository for the latest version
90
91 let provider = source::get_provider(repo_link)?;
92 provider.get_latest_version()
93
94}
95
96pub fn get_link_for_version(repo_link : &str, version : &Version) -> Result<String,Error> {
97 //! Checks the given Repository for the compatible release of the given version.
98 //!
99 //! If there isn't any release for that version then it will return an Error.
100
101 let provider = source::get_provider(repo_link)?;
102 provider.get_link_for(version)
103}
104
105pub fn get_link_for_latest(repo_link : &str) -> Result<(String,Version),Error> {
106 //! Checks the given Repository for the latest compatible release.
107 //!
108 //! Might not necessarily be the latest version but rather is the
109 //! latest version that has a release for the user's platform. Will
110 //! return an error if there is connectivity issues or no releases
111 //! were found for the user's platform.
112
113 let provider = source::get_provider(repo_link)?;
114 provider.get_link_for_latest()
115}
116
117pub fn create_version_raw(version_string : &[u8]) -> Version {
118 //! A passthrough for the _version-lp_ crate, so you can create a version to compare against.
119 //!
120 //! Accepts a reference to an Array of `u8` of any length.
121
122 Version::new(version_string)
123}
124
125pub fn create_version(version_string : &str) -> Option<Version> {
126 //! A passthrough for the _version-lp_ crate, so you can create a version to compare against.
127 //!
128 //! Will attempt to create a version from a string.
129
130 Version::from_str(version_string)
131}
132
133pub fn update_from_link(link : &str) -> Result<(),Error> {
134 //! Updates the current application from the provided link.
135 //!
136 //! It will validate the download and check that it contains an
137 //! executable that is named the same as this program otherwise
138 //! it will fail. This is to prevent the application from deleting
139 //! itself without replacing itself with a proper replacement.
140 //!
141 //! It will not check for platform compatibility. It is assuming that
142 //! already done. Designed to work with
143 //! [get_link_from_latest()](fn.get_link_for_latest.html#func.get_link_for_latest)
144
145 let exe_path = env::current_exe().unwrap();
146
147 let (file,_) = download::download(&link,".")?;
148
149 match exe_path.file_name() {
150 None => { return Err(format_err!("Can't determine executable name")); }
151 Some(file_name) => {
152 if archive::contains_file(&file, file_name.to_str().unwrap())? {
153 match exe_path.parent() {
154 None => { return Err(format_err!("Can't determine executable path")); }
155 Some(parent) => {
156 if let Err(_) = fs::remove_file(&exe_path) {
157 // probably on windows, doesn't let you delete the file if you
158 // are currently running it, unix allows you to do this though
159 let mut renamed_exe = exe_path.clone();
160 renamed_exe.set_extension("old");
161 fs::rename(&exe_path,&renamed_exe)?;
162 }
163 archive::extract_root_to(&file,&parent.display().to_string())?;
164 fs::remove_file(file)?;
165 }
166 }
167 }
168 }
169 }
170 Ok(())
171}
172
173pub fn update_with_version(repo_link : &str, version : &Version) -> Result<(),Error> {
174 //! Update the application with the specific version from the Repository.
175 //!
176 //! Will fail if it cannot find an appropriate version / compatible platform.
177
178 let link = get_link_for_version(repo_link, version)?;
179
180 update_from_link(&link)
181}