1#![deny(unsafe_code)]
18#![doc(test(attr(deny(warnings))))]
20#![deny(
24 clippy::all,
25 clippy::default_trait_access,
26 clippy::expl_impl_clone_on_copy,
27 clippy::if_not_else,
28 clippy::needless_continue,
29 clippy::single_match_else,
30 clippy::unseparated_literal_suffix,
31 clippy::used_underscore_binding
32)]
33#![allow(clippy::match_ref_pats)]
35#![allow(
37 clippy::derived_hash_with_manual_eq,
38 clippy::len_without_is_empty,
39 clippy::redundant_field_names,
40 clippy::too_many_arguments,
41 clippy::single_component_path_imports,
42 clippy::double_must_use
43)]
44#![allow(clippy::new_without_default, clippy::new_ret_no_self)]
46#![allow(clippy::mutex_atomic)]
48
49use displaydoc::Display;
50use thiserror::Error;
51
52use std::{cmp, fmt, ops::Range, path::PathBuf};
53
54#[derive(Debug, Display, Error)]
56pub enum MedusaNameFormatError {
57 NameIsEmpty,
59 NameStartsWithSlash(String),
61 NameStartsWithDotSlash(String),
63 NameEndsWithSlash(String),
65 NameHasDoubleSlash(String),
67}
68
69#[derive(Clone, Debug, PartialEq, Eq, Hash)]
72pub struct EntryName {
73 name: String,
74 components: Vec<Range<usize>>,
75}
76
77impl fmt::Display for EntryName {
78 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "'{}'", self.name) }
79}
80
81impl cmp::PartialOrd for EntryName {
82 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { Some(self.cmp(other)) }
83}
84
85impl cmp::Ord for EntryName {
86 fn cmp(&self, other: &Self) -> cmp::Ordering {
87 self.components_vec().cmp(&other.components_vec())
88 }
89}
90
91impl EntryName {
92 pub(crate) fn is_empty(&self) -> bool { self.name.is_empty() }
93
94 pub(crate) fn empty() -> Self {
95 Self {
96 name: "".to_string(),
97 components: Vec::new(),
98 }
99 }
100
101 pub fn into_string(self) -> String {
102 if self.is_empty() {
103 panic!("attempted to write an empty EntryName!");
104 }
105 self.name
106 }
107
108 pub(crate) fn add_prefix(&mut self, prefix: &Self) {
109 if prefix.is_empty() {
110 return;
111 }
112 self.name = format!("{}/{}", prefix.name, self.name);
113 self.components = Self::split_indices(&self.name);
114 }
115
116 fn split_indices(s: &str) -> Vec<Range<usize>> {
117 let mut prev_begin: usize = 0;
118 let mut components: Vec<Range<usize>> = Vec::new();
119 for (match_start, matched_str) in s.match_indices('/') {
120 components.push(prev_begin..match_start);
121 prev_begin = match_start + matched_str.len();
122 }
123 components.push(prev_begin..s.len());
124 components
125 }
126
127 fn iter_components(&self, range: Range<usize>) -> impl Iterator<Item=&str> {
128 self.components[range].iter().map(|r| &self.name[r.clone()])
129 }
130
131 pub fn validate(name: String) -> Result<Self, MedusaNameFormatError> {
132 if name.is_empty() {
133 Err(MedusaNameFormatError::NameIsEmpty)
134 } else if name.starts_with('/') {
135 Err(MedusaNameFormatError::NameStartsWithSlash(name))
137 } else if name.starts_with("./") {
138 Err(MedusaNameFormatError::NameStartsWithDotSlash(name))
141 } else if name.ends_with('/') {
142 Err(MedusaNameFormatError::NameEndsWithSlash(name))
144 } else if name.contains("//") {
145 Err(MedusaNameFormatError::NameHasDoubleSlash(name))
146 } else {
147 let components = Self::split_indices(&name);
148 Ok(Self { name, components })
149 }
150 }
151
152 pub fn all_components(&self) -> impl Iterator<Item=&str> {
153 self.iter_components(0..self.components.len())
154 }
155
156 fn components_vec(&self) -> Vec<&str> { self.all_components().collect() }
157
158 pub(crate) fn parent_components(&self) -> impl Iterator<Item=&str> {
159 self.iter_components(0..self.components.len() - 1)
160 }
161}
162
163#[derive(Clone, Debug, PartialEq, Eq)]
164pub struct FileSource {
165 pub name: EntryName,
166 pub source: PathBuf,
167}
168
169impl cmp::PartialOrd for FileSource {
174 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { Some(self.cmp(other)) }
175}
176
177impl cmp::Ord for FileSource {
178 fn cmp(&self, other: &Self) -> cmp::Ordering { self.name.cmp(&other.name) }
179}
180
181pub mod destination;
182
183pub mod crawl;
184
185pub mod zip;
186
187pub mod merge;
188
189