151 lines
4.6 KiB
Rust
151 lines
4.6 KiB
Rust
use std::collections::BTreeMap;
|
|
use std::path::Path;
|
|
|
|
use serde::Deserialize;
|
|
use serde::Serialize;
|
|
|
|
use crate::Result;
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
pub(crate) struct FlakeLock {
|
|
pub(crate) root: String,
|
|
pub(crate) version: u8,
|
|
pub(crate) nodes: BTreeMap<String, FlakeLockNode>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
pub(crate) struct FlakeLockNode {
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub(crate) locked: Option<FlakeLockNodeLocked>,
|
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub(crate) original: Option<FlakeLockNodeOriginal>,
|
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub(crate) inputs: Option<BTreeMap<String, FlakeLockInputValue>>,
|
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub(crate) flake: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
pub(crate) struct FlakeLockNodeLocked {
|
|
#[serde(rename = "lastModified")]
|
|
pub(crate) last_modified: i64,
|
|
|
|
#[serde(rename = "narHash")]
|
|
pub(crate) nar_hash: String,
|
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub(crate) url: Option<String>,
|
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub(crate) owner: Option<String>,
|
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub(crate) repo: Option<String>,
|
|
|
|
#[serde(rename = "ref", default, skip_serializing_if = "Option::is_none")]
|
|
pub(crate) git_ref: Option<String>,
|
|
|
|
pub(crate) rev: String,
|
|
|
|
#[serde(rename = "revCount", default, skip_serializing_if = "Option::is_none")]
|
|
pub(crate) rev_count: Option<i64>,
|
|
|
|
#[serde(rename = "type")]
|
|
pub(crate) repo_type: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(tag = "type", rename_all = "lowercase", deny_unknown_fields)]
|
|
pub(crate) enum FlakeLockNodeOriginal {
|
|
Github {
|
|
owner: String,
|
|
|
|
repo: String,
|
|
|
|
#[serde(rename = "ref", default, skip_serializing_if = "Option::is_none")]
|
|
git_ref: Option<String>,
|
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
rev: Option<String>,
|
|
},
|
|
Git {
|
|
url: String,
|
|
},
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(untagged, deny_unknown_fields)]
|
|
pub(crate) enum FlakeLockInputValue {
|
|
String(String),
|
|
List(Vec<String>),
|
|
}
|
|
|
|
pub(crate) async fn parse_flake_lock<F>(flake_path: F) -> Result<FlakeLock>
|
|
where
|
|
F: AsRef<Path>,
|
|
{
|
|
let lock_file_path = flake_path.as_ref().join("flake.lock");
|
|
let contents = tokio::fs::read_to_string(&lock_file_path).await?;
|
|
let parsed_flake_lock = serde_json::from_str(&contents)?;
|
|
Ok(parsed_flake_lock)
|
|
}
|
|
|
|
impl FlakeLock {
|
|
pub(crate) fn get_root_node(&self) -> Result<&FlakeLockNode> {
|
|
let root_node = self.nodes.get(&self.root).ok_or("Root node not found.")?;
|
|
Ok(root_node)
|
|
}
|
|
|
|
pub(crate) fn get_inputs<'lock>(
|
|
&'lock self,
|
|
source_node: &'lock FlakeLockNode,
|
|
) -> Result<BTreeMap<&'lock String, &'lock FlakeLockNode>> {
|
|
let direct_inputs = match &source_node.inputs {
|
|
Some(inputs) => inputs,
|
|
None => {
|
|
return Ok(BTreeMap::new());
|
|
}
|
|
};
|
|
let mut inputs = BTreeMap::new();
|
|
|
|
for (input_name, input_id_or_path) in direct_inputs.iter() {
|
|
inputs.insert(input_name, self.resolve_path(input_id_or_path)?);
|
|
}
|
|
|
|
Ok(inputs)
|
|
}
|
|
|
|
fn resolve_path<'lock>(
|
|
&'lock self,
|
|
input_id_or_path: &'lock FlakeLockInputValue,
|
|
) -> Result<&'lock FlakeLockNode> {
|
|
match input_id_or_path {
|
|
FlakeLockInputValue::String(input_id) => {
|
|
// If the input value is a plain string, then it refers to a node.
|
|
let node = self.nodes.get(input_id).ok_or("Input not found.")?;
|
|
Ok(node)
|
|
}
|
|
FlakeLockInputValue::List(items) => {
|
|
// If the input value is a list, then it refers to a path to an input.
|
|
let mut current_node = self.get_root_node()?;
|
|
for step in items {
|
|
let child_input_id_or_path = current_node
|
|
.inputs
|
|
.as_ref()
|
|
.ok_or("Node without inputs while walking input tree.")?
|
|
.get(step)
|
|
.ok_or("Step not found in inputs while walking input tree")?;
|
|
current_node = self.resolve_path(child_input_id_or_path)?;
|
|
}
|
|
Ok(current_node)
|
|
}
|
|
}
|
|
}
|
|
}
|