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
135
136
|
use std::{fs::create_dir_all, path::PathBuf};
use mingling::{
Groupped,
macros::{chain, dispatcher, pack, pack_err, r_println, renderer, route},
parser::AsPicker,
res::ResExitCode,
};
use rorolala::bucket::{Bucket, NoProtocol};
use serde::Serialize;
use space_system::{Space, SpaceError, SpaceRoot, find_space_root_with};
use crate::{Next, error::ErrorIo, locale::I18nBucketManager, res::current_dir::ResCurrentDir};
pub const EC_BUCKET_CREATE_DIR_NOT_EMPTY: i32 = 2400;
pub const EC_BUCKET_PATH_NOT_PROVIDED: i32 = 2401;
pub const EC_BUCKET_PATH_NOT_DIRECTORY: i32 = 2402;
pub const EC_BUCKET_NESTED: i32 = 2403;
dispatcher!("bucket.init");
dispatcher!("bucket.create");
pack!(StateBucketCreationPrecheck = PathBuf);
pack!(StateBucketCreation = PathBuf);
#[derive(Debug, Groupped, Serialize)]
pub struct ResultBucketCreated {
bucket_path: PathBuf,
}
pack_err!(ErrorDirectoryNotEmpty = PathBuf);
pack_err!(ErrorBucketPathNotProvided = ());
pack_err!(ErrorBucketPathNotDirectory = PathBuf);
pack_err!(ErrorBucketPathNested = PathBuf);
#[chain]
pub fn handle_bucket_init(_args: EntryBucketInit, cwd: &mut ResCurrentDir) -> Next {
// NOTE: It's a dirty operation :D
// Directly extract the value of the cwd resource for use, reducing Clone
// because it's guaranteed that `ResCurrentDir` won't be used after `handle_bucket_init`
let cwd = std::mem::take(&mut cwd.cwd);
StateBucketCreationPrecheck::new(cwd)
}
#[chain]
pub fn handle_bucket_create(args: EntryBucketCreate) -> Next {
let join = route! {
args.pick_or_route::<PathBuf, _>((),
ErrorBucketPathNotProvided::new(()).to_render()
).unpack()
};
StateBucketCreationPrecheck::new(join).to_chain()
}
#[chain]
pub fn handle_state_bucket_creation_precheck(create: StateBucketCreationPrecheck) -> Next {
let path = create.inner;
// Reject if already inside a Bucket space
if find_space_root_with(&path, &Bucket::<NoProtocol>::get_pattern()).is_ok() {
return ErrorBucketPathNested::new(path).to_render();
}
if path.exists() {
if !path.is_dir() {
return ErrorBucketPathNotDirectory::new(path).to_render();
}
if path
.read_dir()
.map(|mut it| it.next().is_some())
.unwrap_or(false)
{
return ErrorDirectoryNotEmpty::new(path).to_render();
}
}
StateBucketCreation::new(path).to_chain()
}
#[chain]
pub fn handle_state_bucket_creation(create: StateBucketCreation) -> Next {
let path = create.inner;
route! {
create_dir_all(&path).map_err(|e| ErrorIo::from(e).to_render())
};
// Use a protocol-less Bucket as a temporary Space for initialization
let bucket_space = Space::<Bucket<NoProtocol>>::new(Bucket::<NoProtocol>::new_local());
// Initialize the Space and capture any SpaceError::Io
let path_to_init = path.clone();
if let Err(SpaceError::Io(error)) = bucket_space.init(path_to_init) {
return ErrorIo::from(error).to_render();
}
ResultBucketCreated { bucket_path: path }.to_render()
}
#[renderer]
pub fn render_result_bucket_created(result: ResultBucketCreated) {
let path = result.bucket_path.to_string_lossy();
r_println!("{}", I18nBucketManager::created(path));
}
#[renderer]
pub fn render_error_directory_not_empty(_err: ErrorDirectoryNotEmpty, ec: &mut ResExitCode) {
r_println!("{}", I18nBucketManager::error_directory_not_empty().trim());
ec.exit_code = EC_BUCKET_CREATE_DIR_NOT_EMPTY;
}
#[renderer]
pub fn render_error_bucket_path_not_provided(
_err: ErrorBucketPathNotProvided,
ec: &mut ResExitCode,
) {
r_println!(
"{}",
I18nBucketManager::error_bucket_path_not_provided().trim()
);
ec.exit_code = EC_BUCKET_PATH_NOT_PROVIDED;
}
#[renderer]
pub fn render_error_bucket_path_not_directory(
_err: ErrorBucketPathNotDirectory,
ec: &mut ResExitCode,
) {
r_println!("{}", I18nBucketManager::error_directory_not_empty().trim());
ec.exit_code = EC_BUCKET_PATH_NOT_DIRECTORY;
}
#[renderer]
pub fn render_error_bucket_path_nested(_err: ErrorBucketPathNested, ec: &mut ResExitCode) {
r_println!("{}", I18nBucketManager::error_bucket_nested().trim());
ec.exit_code = EC_BUCKET_NESTED;
}
|