Skip to content

Commit

Permalink
Use settings.toml with toolchains
Browse files Browse the repository at this point in the history
  • Loading branch information
cnpryer committed Nov 8, 2023
1 parent c17e793 commit f5ebdb4
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 38 deletions.
4 changes: 4 additions & 0 deletions crates/huak-package-manager/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ impl Metadata {
pub fn tool(&self) -> Option<&Table> {
self.tool.as_ref()
}

pub fn tool_mut(&mut self) -> Option<&mut Table> {
self.tool.as_mut()
}
}

impl Default for Metadata {
Expand Down
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

0 comments on commit f5ebdb4

Please sign in to comment.