use std::{
collections::HashMap,
fs::File,
io::{Read, Write},
};
pub mod lock;
pub mod maven_metadata;
pub mod repository;
use anyhow::Context;
use serde::{Deserialize, Serialize};
use toml_edit::Document;
use crate::{
get_project_root,
submodules::resolvers::{get_default_resolvers, NetResolver, Resolver},
};
#[derive(Serialize, Deserialize, Debug)]
pub struct LabToml {
pub project: Project,
pub dependencies: Option<HashMap<String, Dependency>>,
pub resolvers: Option<HashMap<String, ResolverTable>>,
pub plugins: Option<HashMap<String, PluginTable>>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Project {
pub name: String,
pub description: String,
pub version_number: i32,
pub version: String,
pub package: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Dependency {
pub artifact_id: Option<String>,
pub group_id: String,
pub version: String,
pub dep_type: Option<String>,
pub resolver: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ResolverTable {
pub url: String,
#[serde(default)]
pub priority: i32,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct PluginTable {
pub location: Option<String>,
pub version: String,
}
const LABT_TOML_FILE_NAME: &str = "Labt.toml";
const VERSION_STRING: &str = "version";
const GROUP_ID_STRING: &str = "group_id";
const DEPENDENCIES_STRING: &str = "dependencies";
const LOCATION_STRING: &str = "location";
const PLUGINS_STRING: &str = "plugins";
pub fn get_config_string() -> anyhow::Result<String> {
let mut path = get_project_root()
.context("Failed opening the project root directory")?
.clone();
path.push(LABT_TOML_FILE_NAME);
let mut file = File::open(&path).context(format!(
"Failed opening {} at {} is this a valid Labt project directory?",
LABT_TOML_FILE_NAME,
path.into_os_string().into_string().unwrap_or(String::new())
))?;
let mut toml_string = String::new();
file.read_to_string(&mut toml_string)
.context(format!("Failed reading {}", LABT_TOML_FILE_NAME))?;
Ok(toml_string)
}
pub fn get_config() -> anyhow::Result<LabToml> {
let toml_string = get_config_string()?;
let toml: LabToml =
toml::from_str(&toml_string).context(format!("Failed parsing {}", LABT_TOML_FILE_NAME))?;
Ok(toml)
}
pub fn get_editable_config() -> anyhow::Result<Document> {
let toml_string = get_config_string()?;
let toml = toml_string
.parse::<Document>()
.context(format!("Failed parsing {}", LABT_TOML_FILE_NAME))?;
Ok(toml)
}
pub fn get_resolvers() -> anyhow::Result<Vec<Box<dyn Resolver>>> {
let config = get_config().context("Failed to get the project config")?;
get_resolvers_from_config(&config).context("Failed to get resolvers from project config")
}
pub fn get_resolvers_from_config(config: &LabToml) -> anyhow::Result<Vec<Box<dyn Resolver>>> {
let mut resolvers =
get_default_resolvers().context("Failed to initialize default resolvers")?;
if let Some(config_resolvers) = &config.resolvers {
for (name, resolver) in config_resolvers {
let mut net_resolver = NetResolver::init(name.as_str(), resolver.url.as_str())
.context(format!(
"Failed to initialize resolver {} for repo at {}",
name, resolver.url
))?;
net_resolver.set_priority(resolver.priority);
let m_resolver: Box<dyn Resolver> = Box::new(net_resolver);
if let Some((index, _)) = resolvers
.iter()
.enumerate()
.find(|(_, res)| res.get_name() == name.clone())
{
resolvers[index] = m_resolver;
} else {
resolvers.push(m_resolver);
}
}
}
resolvers.sort_by_key(|b| std::cmp::Reverse(b.get_priority()));
Ok(resolvers)
}
pub fn add_dependency_to_config(
group_id: String,
artifact_id: String,
version: String,
) -> anyhow::Result<()> {
use toml_edit::value;
use toml_edit::InlineTable;
use toml_edit::Item;
use toml_edit::Table;
let mut config = get_editable_config()?;
let mut inline_table = InlineTable::new();
inline_table.insert(VERSION_STRING, version.into());
inline_table.insert(GROUP_ID_STRING, group_id.into());
if config.contains_table(DEPENDENCIES_STRING) {
config[DEPENDENCIES_STRING][artifact_id] = value(inline_table);
} else {
let mut table = Table::new();
table.insert(&artifact_id, value(inline_table));
config.insert(DEPENDENCIES_STRING, Item::Table(table));
}
let mut path = std::env::current_dir()?;
path.push(LABT_TOML_FILE_NAME);
let mut file = File::create(path)?;
file.write_all(config.to_string().as_bytes())?;
Ok(())
}
pub fn add_plugin_to_config(name: String, version: String, location: String) -> anyhow::Result<()> {
use toml_edit::value;
use toml_edit::InlineTable;
use toml_edit::Item;
use toml_edit::Table;
let mut config = get_editable_config().context("Failed to get project config")?;
let mut inline_table = InlineTable::new();
inline_table.insert(VERSION_STRING, version.into());
inline_table.insert(LOCATION_STRING, location.into());
if config.contains_table(PLUGINS_STRING) {
config[PLUGINS_STRING][name] = value(inline_table);
} else {
let mut table = Table::new();
table.insert(&name, value(inline_table));
config.insert(PLUGINS_STRING, Item::Table(table));
}
let mut path = std::env::current_dir().context("Failed to get current working directory")?;
path.push(LABT_TOML_FILE_NAME);
let mut file = File::create(path).context(format!(
"Failed to create {} config file",
LABT_TOML_FILE_NAME
))?;
file.write_all(config.to_string().as_bytes())
.context(format!("Failed to write to {} file", LABT_TOML_FILE_NAME))?;
Ok(())
}
pub fn remove_plugin_from_config(name: String) -> anyhow::Result<()> {
let mut config = get_editable_config().context("Failed to get project config")?;
if let Some(table) = config.get_mut(PLUGINS_STRING) {
if let Some(table) = table.as_table_mut() {
table.remove(name.as_str());
let mut path =
std::env::current_dir().context("Failed to get current working directory")?;
path.push(LABT_TOML_FILE_NAME);
let mut file = File::create(path).context(format!(
"Failed to create {} config file",
LABT_TOML_FILE_NAME
))?;
file.write_all(config.to_string().as_bytes())
.context(format!("Failed to write to {} file", LABT_TOML_FILE_NAME))?;
}
}
Ok(())
}
#[test]
fn get_resolvers_from_config_test() {
let config = LabToml {
dependencies: None,
project: Project {
name: String::from("labt"),
description: String::new(),
version_number: 0,
version: String::from("0.0"),
package: String::from("com.gitlab.labtool"),
},
resolvers: Some(HashMap::from([
(
String::from("local"),
ResolverTable {
url: String::from("http://localhost/maven2"),
priority: 99,
},
),
(
String::from("repo1"),
ResolverTable {
url: String::from("http://example.com/maven2"),
priority: 2,
},
),
(
String::from("google"),
ResolverTable {
url: String::from("https://maven.google.com/new-url"),
priority: 11,
},
),
])),
plugins: None,
};
let resolvers = get_resolvers_from_config(&config).expect("Failed to get resolvers");
assert_eq!(resolvers[0].get_name(), String::from("local"));
assert_eq!(resolvers[1].get_name(), String::from("google"));
assert_eq!(resolvers[2].get_name(), String::from("cache"));
assert_eq!(resolvers[3].get_name(), String::from("repo1"));
assert_eq!(resolvers[4].get_name(), String::from("central"));
assert_eq!(
resolvers
.iter()
.map(|res| res.get_priority())
.collect::<Vec<i32>>(),
vec![99, 11, 10, 2, 1]
);
}