use std::string::ToString;
use crate::headers::Header::{XAutoMakeBucket, XKeepOldVersion, XMeta, XQueueDerive, XSizeHint};
use crate::headers::{Header, RequestHeaderExt};
pub mod changes;
pub mod headers;
pub mod tasks;
pub const DEFAULT_USER_AGENT: &'static str = "iars <https://crates.io/crates/iars>";
#[derive(Debug)]
pub enum ItemError {
Ureq(ureq::Error),
Forbidden(ureq::Response),
InvalidIdentifier(String),
}
impl From<ureq::Error> for ItemError {
fn from(value: ureq::Error) -> Self {
match value {
ureq::Error::Status(403, resp) => Self::Forbidden(resp),
_ => Self::Ureq(value)
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Credentials {
pub access: String,
pub secret: String,
}
impl Credentials {
pub fn new(access: &str, secret: &str) -> Self {
Self {
access: access.into(),
secret: secret.into(),
}
}
pub fn try_from_env() -> Option<Self> {
let access = std::env::var("AWS_ACCESS_KEY_ID").ok()?;
let secret = std::env::var("AWS_SECRET_ACCESS_KEY").ok()?;
if access.is_empty() || secret.is_empty() {
return None;
}
Some(Self {
access,
secret,
})
}
}
impl From<&Credentials> for Header {
fn from(value: &Credentials) -> Self {
Header::Authorization {
access: value.access.clone(),
secret: value.secret.clone(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Item {
identifier: String,
credentials: Option<Credentials>,
keep_old_versions: bool,
auto_make_bucket: bool,
use_test_collection: bool,
useragent: String,
}
impl Item {
pub fn new(ident: &str) -> Self {
Self {
identifier: ident.to_string(),
credentials: None,
keep_old_versions: false,
auto_make_bucket: true,
use_test_collection: false,
useragent: DEFAULT_USER_AGENT.to_string(),
}
}
pub fn with_credentials(mut self, credentials: Option<Credentials>) -> Self {
self.credentials = credentials;
self
}
pub fn with_useragent(mut self, useragent: Option<String>) -> Self {
if useragent.is_none() || useragent.as_ref().unwrap().is_empty() {
self.useragent = DEFAULT_USER_AGENT.to_string();
} else {
self.useragent = useragent.unwrap();
}
self
}
pub fn with_keep_old_versions(mut self, keep_old_versions: bool) -> Self {
self.keep_old_versions = keep_old_versions;
self
}
pub fn with_auto_make(mut self, auto_make_bucket: bool) -> Self {
self.auto_make_bucket = auto_make_bucket;
self
}
pub fn upload_file(&self, derive: bool, initial_meta: &[(&str, &str)], filepath: &str, data: &[u8]) -> Result<ureq::Response, ItemError> {
let mut req = ureq::put(&format!("https://s3.us.archive.org/{}/{filepath}", self.identifier))
.set("user-agent", &self.useragent)
.set_header(XKeepOldVersion(self.keep_old_versions))
.set_header(XAutoMakeBucket(self.auto_make_bucket))
.set_header(XQueueDerive(derive))
.set_header(XSizeHint(data.len()));
for (key, val) in initial_meta {
req = req.set_header(XMeta { name: key.to_string(), value: val.to_string() });
}
if let Some(creds) = self.credentials.as_ref() {
req = req.set_header(creds.into());
}
println!("REQUEST:");
for header in req.header_names() {
if let Some(value) = req.header(&header) {
println!(" {header}: {value}");
}
}
Ok(req.send_bytes(data)?)
}
}
pub fn validate_identifier(ident: &str) -> bool {
if ident.is_empty() || ident.len() > 100 {
return false;
}
let mut chars = ident.chars();
if !chars.next().unwrap().is_ascii_alphanumeric() {
return false;
}
for c in chars {
if !(c.is_ascii_alphanumeric() || c == '_' || c == '-' || c == '.') {
return false;
}
}
true
}