aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock56
-rw-r--r--Cargo.toml1
-rw-r--r--src/main.rs10
-rw-r--r--src/utils/mod.rs130
4 files changed, 151 insertions, 46 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 5efa7f7..80c1d16 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -45,6 +45,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
[[package]]
+name = "cbitset"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29b6ad25ae296159fb0da12b970b2fe179b234584d7cd294c891e2bbb284466b"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -93,6 +102,7 @@ dependencies = [
"log",
"merge",
"pretty_env_logger",
+ "rnix",
"serde",
"serde_derive",
"serde_json",
@@ -563,6 +573,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
[[package]]
+name = "rnix"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbbea4c714e5bbf462fa4316ddf45875d8f0e28e5db81050b5f9ce99746c6863"
+dependencies = [
+ "cbitset",
+ "rowan",
+]
+
+[[package]]
+name = "rowan"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ea7cadf87a9d8432e85cb4eb86bd2e765ace60c24ef86e79084dcae5d1c5a19"
+dependencies = [
+ "rustc-hash",
+ "smol_str",
+ "text_unit",
+ "thin-dst",
+]
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -613,6 +651,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
+name = "smol_str"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ca0f7ce3a29234210f0f4f0b56f8be2e722488b95cb522077943212da3b32eb"
+
+[[package]]
name = "socket2"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -651,6 +695,12 @@ dependencies = [
]
[[package]]
+name = "text_unit"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20431e104bfecc1a40872578dbc390e10290a0e9c35fffe3ce6f73c15a9dbfc2"
+
+[[package]]
name = "textwrap"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -660,6 +710,12 @@ dependencies = [
]
[[package]]
+name = "thin-dst"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db3c46be180f1af9673ebb27bc1235396f61ef6965b3fe0dbb2e624deb604f0e"
+
+[[package]]
name = "thiserror"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 209d812..e9d1fa3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -26,6 +26,7 @@ fork = "0.1"
thiserror = "1.0"
toml = "0.5"
yn = "0.1"
+rnix = "0.8"
[[bin]]
diff --git a/src/main.rs b/src/main.rs
index 544f563..be7ad40 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -338,7 +338,7 @@ async fn run_deploy(
extra_build_args: &[String],
) -> Result<(), RunDeployError> {
let to_deploy: Vec<((&str, &utils::data::Node), (&str, &utils::data::Profile))> =
- match (deploy_flake.node, deploy_flake.profile) {
+ match (&deploy_flake.node, &deploy_flake.profile) {
(Some(node_name), Some(profile_name)) => {
let node = match data.nodes.get(node_name) {
Some(x) => x,
@@ -379,7 +379,7 @@ async fn run_deploy(
profiles_list
.into_iter()
- .map(|x| ((node_name, node), x))
+ .map(|x| ((node_name.as_str(), node), x))
.collect()
}
(None, None) => {
@@ -424,7 +424,7 @@ async fn run_deploy(
let mut parts: Vec<(utils::DeployData, utils::DeployDefs)> = Vec::new();
- for ((node_name, node), (profile_name, profile)) in &to_deploy {
+ for ((node_name, node), (profile_name, profile)) in to_deploy {
let deploy_data = utils::make_deploy_data(
&data.generic_settings,
node,
@@ -478,6 +478,8 @@ enum RunError {
CheckDeploymentError(#[from] CheckDeploymentError),
#[error("Failed to evaluate deployment data: {0}")]
GetDeploymentDataError(#[from] GetDeploymentDataError),
+ #[error("Error parsing flake: {0}")]
+ ParseFlakeError(#[from] utils::ParseFlakeError),
#[error("{0}")]
RunDeployError(#[from] RunDeployError),
}
@@ -491,7 +493,7 @@ async fn run() -> Result<(), RunError> {
let opts: Opts = Opts::parse();
- let deploy_flake = utils::parse_flake(opts.flake.as_str());
+ let deploy_flake = utils::parse_flake(opts.flake.as_str())?;
let cmd_overrides = utils::CmdOverrides {
ssh_user: opts.ssh_user,
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index 4fae7a6..deea78e 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -2,6 +2,8 @@
//
// SPDX-License-Identifier: MPL-2.0
+use rnix::{types::*, NodeOrToken, SyntaxKind::*, SyntaxNode};
+
use std::path::PathBuf;
use merge::Merge;
@@ -36,100 +38,144 @@ pub struct CmdOverrides {
#[derive(PartialEq, Debug)]
pub struct DeployFlake<'a> {
pub repo: &'a str,
- pub node: Option<&'a str>,
- pub profile: Option<&'a str>,
+ pub node: Option<String>,
+ pub profile: Option<String>,
}
-pub fn parse_flake(flake: &str) -> DeployFlake {
+#[derive(Error, Debug)]
+pub enum ParseFlakeError {
+ #[error("The given path was too long, did you mean to put something in quotes?")]
+ PathTooLong,
+ #[error("Unrecognized node or token encountered")]
+ Unrecognized,
+}
+pub fn parse_flake(flake: &str) -> Result<DeployFlake, ParseFlakeError> {
let flake_fragment_start = flake.find('#');
let (repo, maybe_fragment) = match flake_fragment_start {
Some(s) => (&flake[..s], Some(&flake[s + 1..])),
None => (flake, None),
};
- let (node, profile) = match maybe_fragment {
- Some(fragment) => {
- let fragment_profile_start = fragment.rfind('.');
-
- match fragment_profile_start {
- Some(s) => (
- Some(&fragment[..s]),
- // Ignore the trailing `.`
- (if (s + 1) == fragment.len() {
- None
- } else {
- Some(&fragment[s + 1..])
- }),
- ),
- None => (Some(fragment), None),
+ let mut node: Option<String> = None;
+ let mut profile: Option<String> = None;
+
+ if let Some(fragment) = maybe_fragment {
+ let ast = rnix::parse(fragment);
+
+ let first_child = match ast.root().node().first_child() {
+ Some(x) => x,
+ None => {
+ return Ok(DeployFlake {
+ repo,
+ node: None,
+ profile: None,
+ })
+ }
+ };
+
+ let mut node_over = false;
+
+ for entry in first_child.children_with_tokens() {
+ let x: Option<String> = match (entry.kind(), node_over) {
+ (TOKEN_DOT, false) => {
+ node_over = true;
+ None
+ }
+ (TOKEN_DOT, true) => {
+ return Err(ParseFlakeError::PathTooLong);
+ }
+ (NODE_IDENT, _) => Some(entry.into_node().unwrap().text().to_string()),
+ (TOKEN_IDENT, _) => Some(entry.into_token().unwrap().text().to_string()),
+ (NODE_STRING, _) => {
+ let c = entry
+ .into_node()
+ .unwrap()
+ .children_with_tokens()
+ .nth(1)
+ .unwrap();
+
+ Some(c.into_token().unwrap().text().to_string())
+ }
+ _ => return Err(ParseFlakeError::Unrecognized),
+ };
+
+ if !node_over {
+ node = x;
+ } else {
+ profile = x;
}
}
- None => (None, None),
- };
+ }
- DeployFlake {
+ Ok(DeployFlake {
repo,
node,
profile,
- }
+ })
}
#[test]
fn test_parse_flake() {
assert_eq!(
- parse_flake("../deploy/examples/system#example"),
+ parse_flake("../deploy/examples/system").unwrap(),
DeployFlake {
repo: "../deploy/examples/system",
- node: Some("example"),
- profile: None
+ node: None,
+ profile: None,
}
);
assert_eq!(
- parse_flake("../deploy/examples/system#example.system"),
+ parse_flake("../deploy/examples/system#").unwrap(),
DeployFlake {
repo: "../deploy/examples/system",
- node: Some("example"),
- profile: Some("system")
+ node: None,
+ profile: None,
}
);
assert_eq!(
- parse_flake("../deploy/examples/system"),
+ parse_flake("../deploy/examples/system#computer.\"something.nix\"").unwrap(),
DeployFlake {
repo: "../deploy/examples/system",
- node: None,
- profile: None,
+ node: Some("computer".to_string()),
+ profile: Some("something.nix".to_string()),
+ }
+ );
+
+ assert_eq!(
+ parse_flake("../deploy/examples/system#\"example.com\".system").unwrap(),
+ DeployFlake {
+ repo: "../deploy/examples/system",
+ node: Some("example.com".to_string()),
+ profile: Some("system".to_string()),
}
);
- // Trailing `.` should be ignored
assert_eq!(
- parse_flake("../deploy/examples/system#example."),
+ parse_flake("../deploy/examples/system#example").unwrap(),
DeployFlake {
repo: "../deploy/examples/system",
- node: Some("example"),
+ node: Some("example".to_string()),
profile: None
}
);
- // The last `.` should be used for splitting
assert_eq!(
- parse_flake("../deploy/examples/system#example.com.system"),
+ parse_flake("../deploy/examples/system#example.system").unwrap(),
DeployFlake {
repo: "../deploy/examples/system",
- node: Some("example.com"),
- profile: Some("system")
+ node: Some("example".to_string()),
+ profile: Some("system".to_string())
}
);
- // The last `.` should be used for splitting, _and_ trailing `.` should be ignored
assert_eq!(
- parse_flake("../deploy/examples/system#example.com."),
+ parse_flake("../deploy/examples/system").unwrap(),
DeployFlake {
repo: "../deploy/examples/system",
- node: Some("example.com"),
- profile: None
+ node: None,
+ profile: None,
}
);
}