File: prepare migration out of flatenned kind

This commit is contained in:
Mikaël Cluseau
2026-05-03 11:34:38 +02:00
parent 71f4faa8cb
commit c0c2c4afdf
4 changed files with 140 additions and 79 deletions
+23 -14
View File
@@ -1,12 +1,20 @@
use eyre::Result;
use eyre::{Result, format_err};
use log::info;
use std::path::Path;
use tokio::fs;
use crate::base64_decode;
use crate::{base64_decode, File};
pub async fn files(files: &[crate::File], root: &str, dry_run: bool) -> Result<()> {
for file in files {
pub async fn files(files: &[File], root: &str, dry_run: bool) -> Result<()> {
for f in files {
if let Err(e) = file(f, root, dry_run).await {
return Err(format_err!("{}: {e}", f.path))
}
}
Ok(())
}
pub async fn file(file: &File, root: &str, dry_run: bool) -> Result<()> {
let path = chroot(root, &file.path);
let path = Path::new(&path);
@@ -15,7 +23,11 @@ pub async fn files(files: &[crate::File], root: &str, dry_run: bool) -> Result<(
}
use crate::{FileKind as K, FilePart as P};
match &file.kind {
match file.kind().as_ref() {
K::Skip => {
info!("{}: kind is skip", file.path);
return Ok(())
},
K::Content(content) => {
if dry_run {
info!(
@@ -57,38 +69,35 @@ pub async fn files(files: &[crate::File], root: &str, dry_run: bool) -> Result<(
fs::write(path, assembly).await?
}
}
K::Dir(true) => {
K::Dir => {
if dry_run {
info!("would create {} (directory)", file.path);
} else {
fs::create_dir(path).await?;
}
}
K::Dir(false) => {} // shouldn't happen, but semantic is to ignore
K::Symlink(tgt) => {
if dry_run {
info!("would create {} (symlink to {})", file.path, tgt);
} else {
let _ = fs::remove_file(path).await; // we're ln --force
fs::symlink(tgt, path).await?;
}
}
}
if dry_run {
continue;
return Ok(());
}
match file.kind {
K::Symlink(_) => {}
_ => set_perms(path, file.mode).await?,
if file.is_symlink() {
set_perms(path, file.mode).await?;
}
info!("created {}", file.path);
Ok(())
}
Ok(())
}
pub async fn set_perms(path: impl AsRef<Path>, mode: Option<u32>) -> std::io::Result<()> {
if let Some(mode) = mode.filter(|m| *m != 0) {
use std::os::unix::fs::PermissionsExt;
+53 -7
View File
@@ -1,3 +1,5 @@
use std::borrow::Cow;
pub mod apply;
pub mod bootstrap;
pub mod cgroup;
@@ -56,26 +58,41 @@ pub struct User {
pub gid: Option<u32>,
}
#[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
#[derive(Default, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
pub struct File {
pub path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub mode: Option<u32>,
#[serde(flatten)]
pub kind: FileKind,
pub kind: Option<FileKind>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content64: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub symlink: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parts: Option<Vec<FilePart>>,
#[serde(default, skip_serializing_if = "is_false")]
pub dir: bool,
}
#[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
fn is_false(b: &bool) -> bool {
!b
}
#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "lowercase")]
pub enum FileKind {
#[default]
Skip,
Content(String),
Content64(String),
Symlink(String),
Dir(bool),
Parts(Vec<FilePart>),
Symlink(String),
Dir,
}
#[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "lowercase")]
pub enum FilePart {
Content(String),
@@ -102,3 +119,32 @@ pub fn base64_encode(b: &[u8]) -> String {
use base64::{prelude::BASE64_STANDARD as B64, Engine as _};
B64.encode(b)
}
impl<'t> File {
pub fn kind(&'t self) -> Cow<'t, FileKind> {
self.kind.as_ref().map(Cow::Borrowed).unwrap_or_else(|| {
use FileKind::*;
Cow::Owned(if let Some(ref s) = self.content {
Content(s.clone())
} else if let Some(ref s) = self.content64 {
Content64(s.clone())
} else if let Some(ref s) = self.symlink {
Symlink(s.clone())
} else if let Some(ref p) = self.parts {
Parts(p.clone())
} else if self.dir {
Dir
} else {
Skip
})
})
}
pub fn is_symlink(&self) -> bool {
if let Some(ref kind) = self.kind {
matches!(kind, FileKind::Symlink(_))
} else {
self.symlink.is_some()
}
}
}