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}