subcomponent 0.1.0

A components orchestrator
/*
 * Copyright (c) 2016-2017 Jean Guyomarc'h
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

extern crate std;
extern crate getopts;

use compiler;
use cmd;
use config;
use fetcher;

const PROLOG: &'static str =
"Usage:
    subcomponent fetch [options] [components]
";


pub struct CmdFetch {
    dry_run: bool,
    force: bool,
    components: Vec<String>,
}

impl CmdFetch {
   fn fetch_new_component(&self, component: &config::Component) -> Result<(), fetcher::Error> {
      info!("Fetching new component {} ...", component.id_get());

      /*
       * Try sequentially all the available methpds.
       */
      for method in component.fetch_methods_get() {
         debug!("Trying method `{}`...", method.name_get());
         let result = method.fetch_new(component);
         if result.is_ok() {
            return result;
         }
      }

      Err(fetcher::Error::EverythingFailed)
   }

   fn get_used_fetch_method(&self, component: &config::Component) -> Option<String> {
      for method in component.fetch_methods_get() {
         if method.is_fetchable(component) {
            return Some(method.name_get().to_string());
         }
      }
      None
   }

   fn fetch_update_component(&self, component: &config::Component) -> Result<(), fetcher::Error> {
      info!("Updating component \"{}\" ...", component.id_get());
      if let Some(name) = self.get_used_fetch_method(component) {
         let method = component.fetch_method_get(name.as_str());
         method.fetch_update(component, self.force)
      } else {
         Ok(()) /* FIXME Error */
      }
   }

    fn fetch_component(&self, component: &config::Component) -> Result<(), fetcher::Error> {

        let path = std::path::Path::new(component.path_get());
        if path.exists() {
           self.fetch_update_component(component)
        } else {
           self.fetch_new_component(component)
        }
    }
}

impl cmd::Cmd for CmdFetch {
    fn help(&self, opts: &getopts::Options) {
        println!("{}", opts.usage(PROLOG));
    }

    fn getopts_set(&self, opts: &mut getopts::Options) {
        opts.optflag("n", "dry-run", "Don't fetch anything");
        opts.optflag("f", "force",
                     "Force updating. This discards potential user changes!");
    }

    fn getopts_matches_use(&mut self, matches: &getopts::Matches) {
       if matches.opt_present("dry-run") {
           self.dry_run = true;
       }
       if matches.opt_present("force") {
           self.force = true;
       }
       for component in &matches.free {
          self.components.push(component.clone());
       }
    }

    fn run(&self, parser_arg: &Option<compiler::parser::Parser>) -> Result<(), cmd::Error> {
        if let Some(ref parser) = *parser_arg {
           if ! self.dry_run  {
              let components = parser.components_get();

              if ! self.components.is_empty() {
                 /*
                  * If we have provided a list of components to the fetch command,
                  * go through them.
                  */
                 for comp_id in &self.components {
                    let mut comp_found = false;
                    for component in components.iter() {
                       let borrowed = component.borrow();
                       if borrowed.id_get() == comp_id {
                          comp_found = true;
                          self.fetch_component(&borrowed)?;
                          break;
                       }
                    }
                    if ! comp_found {
                       error!("Failed to find component '{}'", comp_id);
                       return Err(cmd::Error::InvalidComponentName);
                    }
                 }
              } else {
                 /*
                  * No list of components was provided, fetch everything!
                  */
                 for component in components.iter() {
                    let borrowed = component.borrow();
                    self.fetch_component(&borrowed)?;
                 }
              }
           }
           Ok(())
        } else {
           error!("The 'fetch' command requires a valid subcomponent file");
           Err(cmd::Error::NoSubcomponentFile)
        }
    }
}

pub fn new() -> CmdFetch {
    /* Create a fetch command with its default values */
    CmdFetch {
        dry_run: false,
        force: false,
        components: Vec::new(),
    }
}