cargo_reg/
config.rs

1//! Main functionality for operating on alternative registries in `.cargo/config`.
2
3use std::collections::HashMap;
4
5use toml_edit::{self, Document};
6use failure::Error;
7
8use errors::*;
9
10const REGISTRIES_ENTRY: &str = "registries";
11
12/// Registries view: <ALIAS> => <INDEX_URL>
13pub type Registries = HashMap<String, String>;
14
15/// Representaion of `.cargo/config`.
16/// All the modifications are performed in the memory, so the `Config` struct
17/// should be written into a file afterwards using `config_location::write_config()`.
18#[derive(Debug)]
19pub struct Config {
20    data: Document,
21}
22
23impl Config {
24    /// Construct `Config` from content of the config file
25    pub fn new(data: String) -> Result<Config, Error> {
26        match data.parse() {
27            Ok(data) => Ok(Config { data }),
28            Err(_) => Err(RegistryError::BrokenConfig.into())
29        }
30    }
31
32    /// Return current state as String
33    pub fn data(&self) -> String {
34        self.data.to_string()
35    }
36
37    /// Add a new alias and URL
38    pub fn add(&mut self, name: &str, url: &str) -> Result<(), Error> {
39        match self.check_table() {
40            Ok(_) => {
41                if !self.data[REGISTRIES_ENTRY][name].is_none() {
42                    return Err(RegistryError::AlreadyExist { name: name.into() }.into());
43                }
44            },
45            Err(_) => { self.data[REGISTRIES_ENTRY] = toml_edit::table(); }
46        }
47
48        self.data[REGISTRIES_ENTRY][name] = toml_edit::value(url);
49        Ok(())
50    }
51
52    /// Remove an alias
53    pub fn remove(&mut self, name: &str) -> Result<String, Error> {
54        self.check_table()?;
55
56        let registries = &mut self.data[REGISTRIES_ENTRY];
57        if registries[name].is_none() {
58            return Err(RegistryError::NoSuchRegistry { name: name.into() }.into());
59        }
60
61        let url = registries[name].as_str().unwrap().to_string();
62        registries[name] = toml_edit::Item::None;
63
64        // remove table if empty
65        if registries.as_table_like().unwrap().is_empty() {
66            *registries = toml_edit::Item::None;
67        }
68
69        Ok(url)
70    }
71
72    /// Rename an existing alias
73    pub fn rename(&mut self, old: &str, new: &str) -> Result<(), Error> {
74        let url = self.get(old)?;
75        self.add(new, &url)?;
76        let _ = self.remove(old)?;
77        Ok(())
78    }
79
80    /// Get the URL for a given alias
81    pub fn get(&self, name: &str) -> Result<String, Error> {
82        self.check_table()?;
83
84        let registry = &self.data[REGISTRIES_ENTRY][name];
85        if registry.is_none() {
86            return Err(RegistryError::NoSuchRegistry { name: name.into() }.into());
87        }
88
89        Ok(registry.as_str().unwrap().to_string())
90    }
91
92    /// Set a new URL for existing alias
93    pub fn set(&mut self, name: &str, url: &str) -> Result<String, Error> {
94        self.check_table()?;
95
96        let registry = &mut self.data[REGISTRIES_ENTRY][name];
97        if registry.is_none() {
98            return Err(RegistryError::NoSuchRegistry { name: name.into() }.into());
99        }
100
101        let old = registry.as_str().unwrap().to_string();
102        *registry = toml_edit::value(url);
103
104        Ok(old)
105    }
106
107    /// List all the alias.
108    /// * --global - list ONLY global config
109    /// * --local  - list ONLY local config
110    /// * both     - error, cannot use both the flags
111    /// * nothing  - list global config but overwrite with local value; shows configuration for
112    /// current directory
113    pub fn list(&self) -> Result<Registries, Error> {
114        let mut result = HashMap::new();
115        let registries = match &self.data[REGISTRIES_ENTRY].as_table() {
116            &Some(r) => r,
117            &None => return Ok(result),
118        };
119
120        for (s, v) in registries.iter() {
121            if let Some(v) = v.as_str() {
122                result.insert(s.to_string(), v.to_string());
123            }
124        }
125
126        Ok(result)
127    }
128
129    /// Helper that checks an existence of the table
130    fn check_table(&self) -> Result<(), Error> {
131        let reg = &self.data[REGISTRIES_ENTRY];
132        if !reg.is_table_like() {
133            return Err(RegistryError::TableAbsent.into());
134        }
135
136        Ok(())
137    }
138}