You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
5.6 KiB
Rust
215 lines
5.6 KiB
Rust
use std::collections::HashMap;
|
|
use std::fmt::Debug;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use buildkit_proto::pb;
|
|
|
|
use super::path::{LayerPath, UnsetPath};
|
|
use super::FileOperation;
|
|
|
|
use crate::serialization::{Context, Result};
|
|
use crate::utils::OutputIdx;
|
|
|
|
#[derive(Debug)]
|
|
pub struct CopyOperation<From: Debug, To: Debug> {
|
|
source: From,
|
|
destination: To,
|
|
|
|
follow_symlinks: bool,
|
|
recursive: bool,
|
|
create_path: bool,
|
|
wildcard: bool,
|
|
|
|
description: HashMap<String, String>,
|
|
caps: HashMap<String, bool>,
|
|
}
|
|
|
|
type OpWithoutSource = CopyOperation<UnsetPath, UnsetPath>;
|
|
type OpWithSource<'a> = CopyOperation<LayerPath<'a, PathBuf>, UnsetPath>;
|
|
type OpWithDestination<'a> =
|
|
CopyOperation<LayerPath<'a, PathBuf>, (OutputIdx, LayerPath<'a, PathBuf>)>;
|
|
|
|
impl OpWithoutSource {
|
|
pub(crate) fn new() -> OpWithoutSource {
|
|
let mut caps = HashMap::<String, bool>::new();
|
|
caps.insert("file.base".into(), true);
|
|
|
|
CopyOperation {
|
|
source: UnsetPath,
|
|
destination: UnsetPath,
|
|
|
|
follow_symlinks: false,
|
|
recursive: false,
|
|
create_path: false,
|
|
wildcard: false,
|
|
|
|
caps,
|
|
description: Default::default(),
|
|
}
|
|
}
|
|
|
|
pub fn from<P>(self, source: LayerPath<'_, P>) -> OpWithSource
|
|
where
|
|
P: AsRef<Path>,
|
|
{
|
|
CopyOperation {
|
|
source: source.into_owned(),
|
|
destination: UnsetPath,
|
|
|
|
follow_symlinks: self.follow_symlinks,
|
|
recursive: self.recursive,
|
|
create_path: self.create_path,
|
|
wildcard: self.wildcard,
|
|
|
|
description: self.description,
|
|
caps: self.caps,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> OpWithSource<'a> {
|
|
pub fn to<P>(self, output: OutputIdx, destination: LayerPath<'a, P>) -> OpWithDestination<'a>
|
|
where
|
|
P: AsRef<Path>,
|
|
{
|
|
CopyOperation {
|
|
source: self.source,
|
|
destination: (output, destination.into_owned()),
|
|
|
|
follow_symlinks: self.follow_symlinks,
|
|
recursive: self.recursive,
|
|
create_path: self.create_path,
|
|
wildcard: self.wildcard,
|
|
|
|
description: self.description,
|
|
caps: self.caps,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> OpWithDestination<'a> {
|
|
pub fn into_operation(self) -> super::sequence::SequenceOperation<'a> {
|
|
super::sequence::SequenceOperation::new().append(self)
|
|
}
|
|
}
|
|
|
|
impl<From, To> CopyOperation<From, To>
|
|
where
|
|
From: Debug,
|
|
To: Debug,
|
|
{
|
|
pub fn follow_symlinks(mut self, value: bool) -> Self {
|
|
self.follow_symlinks = value;
|
|
self
|
|
}
|
|
|
|
pub fn recursive(mut self, value: bool) -> Self {
|
|
self.recursive = value;
|
|
self
|
|
}
|
|
|
|
pub fn create_path(mut self, value: bool) -> Self {
|
|
self.create_path = value;
|
|
self
|
|
}
|
|
|
|
pub fn wildcard(mut self, value: bool) -> Self {
|
|
self.wildcard = value;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl<'a> FileOperation for OpWithDestination<'a> {
|
|
fn output(&self) -> i32 {
|
|
self.destination.0.into()
|
|
}
|
|
|
|
fn serialize_inputs(&self, cx: &mut Context) -> Result<Vec<pb::Input>> {
|
|
let mut inputs = if let LayerPath::Other(ref op, ..) = self.source {
|
|
let serialized_from_head = cx.register(op.operation())?;
|
|
|
|
vec![pb::Input {
|
|
digest: serialized_from_head.digest.clone(),
|
|
index: op.output().into(),
|
|
}]
|
|
} else {
|
|
vec![]
|
|
};
|
|
|
|
if let LayerPath::Other(ref op, ..) = self.destination.1 {
|
|
let serialized_to_head = cx.register(op.operation())?;
|
|
|
|
inputs.push(pb::Input {
|
|
digest: serialized_to_head.digest.clone(),
|
|
index: op.output().into(),
|
|
});
|
|
}
|
|
|
|
Ok(inputs)
|
|
}
|
|
|
|
fn serialize_action(
|
|
&self,
|
|
inputs_count: usize,
|
|
inputs_offset: usize,
|
|
) -> Result<pb::FileAction> {
|
|
let (src_idx, src_offset, src) = match self.source {
|
|
LayerPath::Scratch(ref path) => (-1, 0, path.to_string_lossy().into()),
|
|
|
|
LayerPath::Other(_, ref path) => {
|
|
(inputs_offset as i64, 1, path.to_string_lossy().into())
|
|
}
|
|
|
|
LayerPath::Own(ref output, ref path) => {
|
|
let output: i64 = output.into();
|
|
|
|
(
|
|
inputs_count as i64 + output,
|
|
0,
|
|
path.to_string_lossy().into(),
|
|
)
|
|
}
|
|
};
|
|
|
|
let (dest_idx, dest) = match self.destination.1 {
|
|
LayerPath::Scratch(ref path) => (-1, path.to_string_lossy().into()),
|
|
|
|
LayerPath::Other(_, ref path) => (
|
|
inputs_offset as i32 + src_offset,
|
|
path.to_string_lossy().into(),
|
|
),
|
|
|
|
LayerPath::Own(ref output, ref path) => {
|
|
let output: i32 = output.into();
|
|
|
|
(inputs_count as i32 + output, path.to_string_lossy().into())
|
|
}
|
|
};
|
|
|
|
Ok(pb::FileAction {
|
|
input: i64::from(dest_idx),
|
|
secondary_input: src_idx,
|
|
|
|
output: i64::from(self.output()),
|
|
|
|
action: Some(pb::file_action::Action::Copy(pb::FileActionCopy {
|
|
src,
|
|
dest,
|
|
|
|
follow_symlink: self.follow_symlinks,
|
|
dir_copy_contents: self.recursive,
|
|
create_dest_path: self.create_path,
|
|
allow_wildcard: self.wildcard,
|
|
|
|
// TODO: make this configurable
|
|
mode: -1,
|
|
|
|
// TODO: make this configurable
|
|
timestamp: -1,
|
|
|
|
..Default::default()
|
|
})),
|
|
})
|
|
}
|
|
}
|