1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
use std::{
fs::{DirEntry, Metadata},
path::{Path, PathBuf},
};
use super::prelude::*;
/// Creates links
///
/// If a link is created for a file that already had a link then the old link will be overwritten.
/// By default creating a link to a directory will recursively link all files under that
/// directory, to actually link a directory use the --directory flag.
#[derive(Parser)]
pub struct Opts {
/// Treats the paths as directories. This will create links to the actual directories instead
/// of recursively linking all files under them.
#[clap(long)]
directory: bool,
/// The paths to link. The last path is the destination.
paths: Vec<PathBuf>,
}
// TODO: require destination
// remove else branch
pub fn main(config: Config, opts: Opts) -> anyhow::Result<()> {
let mut depot = utils::read_depot(&config.archive_path)?;
let (origins, destination) = match opts.paths.as_slice() {
p @ [] | p @ [_] => (p, None),
[o @ .., dest] => (o, Some(dest)),
_ => unreachable!(),
};
if let Some(destination) = destination {
let params = if opts.directory {
origins
.iter()
.map(|p| LinkCreateParams {
origin: p.to_path_buf(),
destination: destination.clone(),
})
.collect()
} else {
let mut params = Vec::new();
for origin in origins {
generate_link_params(&depot, origin, destination, origin, &mut params)?;
}
params
};
for link_params in params {
log::info!("Creating link : {}", link_params);
depot.create_link(link_params)?;
}
} else {
let base_path = match origins {
[] => std::env::current_dir()?,
[path] => path.clone(),
_ => unreachable!(),
};
for link in utils::collect_links_by_base_paths(&depot, std::iter::once(base_path)) {
log::info!("{}", link);
}
}
utils::write_depot(&depot)?;
Ok(())
}
fn generate_link_params(
depot: &Depot,
origin: &Path,
destination: &Path,
base: &Path,
params: &mut Vec<LinkCreateParams>,
) -> anyhow::Result<()> {
let metadata = std::fs::metadata(origin)?;
if metadata.is_file() {
generate_file_link_params(depot, origin, destination, base, params)?;
} else if metadata.is_dir() {
generate_directory_link_params_recursive(depot, origin, destination, base, params)?;
}
Ok(())
}
fn generate_file_link_params(
depot: &Depot,
origin: &Path,
destination: &Path,
base: &Path,
params: &mut Vec<LinkCreateParams>,
) -> anyhow::Result<()> {
let origin_canonical = origin
.canonicalize()
.expect("Failed to canonicalize origin path");
let base_canonical = base
.canonicalize()
.expect("Failed to canonicalize base path");
log::debug!("Origin canonical : {}", origin_canonical.display());
log::debug!("Base : {}", base.display());
let partial = origin_canonical
.strip_prefix(base_canonical)
.expect("Failed to remove prefix from origin path");
let destination = destination.join(partial);
let origin = origin_canonical
.strip_prefix(depot.base_path())
.unwrap_or(&origin_canonical);
let link_params = LinkCreateParams {
origin: origin.to_path_buf(),
destination,
};
params.push(link_params);
Ok(())
}
fn generate_directory_link_params_recursive(
depot: &Depot,
dir_path: &Path,
destination: &Path,
base: &Path,
params: &mut Vec<LinkCreateParams>,
) -> anyhow::Result<()> {
for origin in dir_path.read_dir()? {
let origin = origin?.path();
generate_link_params(depot, &origin, destination, base, params)?;
}
Ok(())
}
|