Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use settings.toml with toolchains #831

Merged
merged 1 commit into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 17 additions & 18 deletions crates/huak-package-manager/src/ops/toolchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use std::{
str::FromStr,
};
use termcolor::Color;
use toml_edit::value;

/// Resolve the target toolchain if a user provides one, otherwise get the current toolchain
/// for the current workspace. If no toolchain is found then emit "error: no toolchain found".
Expand Down Expand Up @@ -125,19 +124,6 @@ pub fn install_toolchain(
teardown(parent.join(&channel_string), config)?;
Err(e)
} else {
let settings = parent.join("settings.toml");
let Some(mut db) = SettingsDb::try_from(settings)
.ok()
.or(Some(SettingsDb::default()))
else {
return Err(Error::InternalError(
"failed to create settings db".to_string(),
));
};
let table = db.doc_mut().as_table_mut();
let key = format!("{}", config.cwd.display());
table["toolchains"][key] = value(format!("{}", path.display()));

Ok(())
}
}
Expand Down Expand Up @@ -524,10 +510,23 @@ pub fn update_toolchain(
terminal.print_custom("Success", "finished updating", Color::Green, true)
}

pub fn use_toolchain(_channel: &Channel, _config: &Config) -> HuakResult<()> {
// Resolve the target toolchain if a user provides one, otherwise get the current toolchain
// for the current workspace. If none can be found then install and use the default toolchain.
todo!()
// Resolve the target toolchain if a user provides one, otherwise get the current toolchain
// for the current workspace. If none can be found then install and use the default toolchain.
// Update the settings.toml with the scope that should *use* the resolved toolchain.
pub fn use_toolchain(channel: &Channel, config: &Config) -> HuakResult<()> {
let ws = config.workspace();

let Some(home) = config.home.as_ref() else {
return Err(Error::HuakHomeNotFound);
};

let toolchain = ws.resolve_local_toolchain(Some(channel))?;
let settings = home.join("toolchains").join("settings.toml");
let mut db = SettingsDb::try_from(&settings).unwrap_or_default();

db.insert_scope(ws.root(), toolchain.root());

Ok(db.save(settings)?)
}

fn resolve_installed_toolchains(config: &Config) -> Option<Vec<LocalToolchain>> {
Expand Down
17 changes: 7 additions & 10 deletions crates/huak-package-manager/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use crate::{
use huak_toolchain::{Channel, LocalToolchain, LocalToolchainResolver, SettingsDb};
use huak_workspace::{resolve_first, PathMarker};
use std::{path::PathBuf, process::Command};
use toml_edit::{Item, Value};

/// The `Workspace` is a struct for resolving things like the current `Package`
/// or the current `PythonEnvironment`. It can also provide a snapshot of the `Environment`,
Expand Down Expand Up @@ -221,15 +220,13 @@ fn resolve_local_toolchain(
};
};

// Attempt to retrieve the toolchain for the current workspace scope.
if let Some(table) = SettingsDb::try_from(settings)
.ok()
.as_ref()
.and_then(|db| db.doc().as_table()["scopes"].as_table())
{
if let Some(Item::Value(Value::String(s))) = table.get(&format!("{}", config.cwd.display()))
{
return Some(LocalToolchain::new(PathBuf::from(s.to_string())));
// Attempt to retrieve the toolchain for the current workspace scope by resolving for
// the first matching path from cwd.
if let Some(db) = SettingsDb::try_from(settings).ok().as_ref() {
for p in config.cwd.ancestors() {
if let Some((_, value)) = db.get_scope_entry(p) {
return Some(LocalToolchain::new(PathBuf::from(value.to_string())));
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/huak-toolchain/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ pub enum Error {
LocalToolNotFound(PathBuf),
#[error("a toolchain already exists: {0}")]
LocalToolchainExistsError(PathBuf),
#[error("a problem with utf-8 parsing occurred: {0}")]
Utf8Error(#[from] std::str::Utf8Error),
#[error("{0}")]
TOMLEditError(#[from] toml_edit::TomlError),
#[error("a problem with utf-8 parsing occurred: {0}")]
Utf8Error(#[from] std::str::Utf8Error),
}
87 changes: 83 additions & 4 deletions crates/huak-toolchain/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ use toml_edit::Document;

#[derive(Default)]
pub struct SettingsDb {
doc: Document,
doc: Document, // TODO(cnpryer): Decouple from toml_edit here
}

impl SettingsDb {
#[must_use]
pub fn new(doc: Document) -> Self {
Self { doc }
pub fn new() -> Self {
Self {
doc: Document::new(),
}
}

#[must_use]
Expand All @@ -24,7 +26,52 @@ impl SettingsDb {
}

pub fn try_from<T: AsRef<Path>>(path: T) -> Result<Self, Error> {
Ok(SettingsDb::new(read_settings_file(path)?))
let mut db = Self::new();
db.doc = read_settings_file(path)?;
Ok(db)
}

/// Insert a scope entry.
///
/// ```rust
/// use huak_toolchain::SettingsDb;
/// use std::path::PathBuf;
///
/// let mut db = SettingsDb::new();
/// let cwd = PathBuf::new();
/// let channel = "3.12";
///
/// db.insert_scope(cwd, channel);
/// ```
pub fn insert_scope<T: AsRef<Path>>(&mut self, key: T, value: T) {
let key = format!("{}", key.as_ref().display());
let value = format!("{}", value.as_ref().display());

self.doc_mut()["scopes"][key] = toml_edit::value(value);
}

pub fn remove_scope<T: AsRef<Path>>(&mut self, key: T) {
let key = format!("{}", key.as_ref().display());

self.doc_mut()
.get_mut("scopes")
.and_then(|it| it.as_inline_table_mut()) // TODO(cnpryer): Don't inline
.and_then(|it| it.remove(&key));
}

// TODO(cnpryer): Potentially use `ScopeEntry`.
#[must_use]
pub fn get_scope_entry<T: AsRef<Path>>(&self, key: T) -> Option<(T, String)> {
let k = format!("{}", key.as_ref().display());

// TODO(cnpryer): Smarter escape
self.doc()
.get("scopes")
.and_then(|it| it.get(k).map(|v| (key, escape_string(&v.to_string()))))
}

pub fn save<T: AsRef<Path>>(&self, to: T) -> Result<(), Error> {
write_settings_file(self.doc(), to)
}
}

Expand All @@ -34,3 +81,35 @@ pub(crate) fn read_settings_file<T: AsRef<Path>>(path: T) -> Result<Document, Er

Ok(doc)
}

pub(crate) fn write_settings_file<T: AsRef<Path>>(doc: &Document, path: T) -> Result<(), Error> {
Ok(std::fs::write(path, doc.to_string())?)
}

pub fn escape_string(s: &str) -> String {
s.trim().replace(['\\', '"'], "")
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_scopes() {
let mut db = SettingsDb::new();

db.insert_scope("/", "default");

let (_, value) = db.get_scope_entry("/").unwrap();

assert_eq!(value.to_string(), toml_edit::value("default").to_string());

db.remove_scope("/");

let table = db.doc().get("scopes").unwrap();

assert!(table
.as_inline_table()
.map_or(false, toml_edit::InlineTable::is_empty));
}
}
5 changes: 1 addition & 4 deletions dev-resources/planning.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,9 @@ Huak's toolchain management system can allow for target directories to contain s
```toml
[scopes]
"/" = "default" # Resolve to "default" for any paths under the root (sets "default" as default)
"some/project/path" = "channel or path"
"some/project/path" = "toolchain path"
```

Keyed channels and paths would resolve in the directory the settings.toml is located.



# Huak Workspaces

Expand Down
Loading