parent
5a22c30370
commit
dd1a68f714
@ -1,72 +1,148 @@ |
||||
use serde::Serialize; |
||||
use std::borrow::Cow; |
||||
use std::fmt; |
||||
use std::io::Read; |
||||
use std::path::{Path, PathBuf}; |
||||
|
||||
/// A builder pattern struct for constructing a media attachment.
|
||||
#[derive(Debug, Default, Clone, Serialize)] |
||||
#[derive(Debug)] |
||||
/// A builder pattern struct for preparing a single attachment for upload.
|
||||
///
|
||||
/// For more details, see [`new_media()`](struct.Mastodon.html#method.new_media).
|
||||
pub struct MediaBuilder { |
||||
/// The file name of the attachment to be uploaded.
|
||||
pub file: Cow<'static, str>, |
||||
/// The alt text of the attachment.
|
||||
pub description: Option<Cow<'static, str>>, |
||||
/// The focus point for images.
|
||||
pub focus: Option<(f32, f32)>, |
||||
/// The media attachment itself
|
||||
pub data: MediaBuilderData, |
||||
|
||||
/// The filename to send to the server
|
||||
pub filename: Option<String>, |
||||
|
||||
/// Mimetype to send to the server, identifying what is in the attachment.
|
||||
///
|
||||
/// The string should be a valid mimetype.
|
||||
pub mimetype: Option<String>, |
||||
|
||||
/// Plain text description of the attached piece of media, for accessibility
|
||||
pub description: Option<String>, |
||||
|
||||
/// (x, y) focus point, used by clients to determine how to crop an image
|
||||
pub focus: Option<(f64, f64)>, |
||||
} |
||||
|
||||
/// Enum representing possible sources of attachments to upload
|
||||
pub enum MediaBuilderData { |
||||
/// An arbitrary reader. It is useful for reading from media already in memory.
|
||||
Reader(Box<dyn Read + Send>), |
||||
|
||||
/// Variant represening a file path of the file to attach.
|
||||
File(PathBuf), |
||||
} |
||||
|
||||
impl fmt::Debug for MediaBuilderData { |
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
||||
match self { |
||||
MediaBuilderData::File(f) => fmt.debug_tuple("File").field(&f).finish(), |
||||
MediaBuilderData::Reader(_) => fmt |
||||
.debug_tuple("Reader") |
||||
.field(&format_args!("...")) |
||||
.finish(), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl MediaBuilder { |
||||
/// Create a new attachment from a file name.
|
||||
pub fn new(file: Cow<'static, str>) -> Self { |
||||
/// Create a new MediaBuilder from a reader `data`
|
||||
pub fn from_reader<R: Read + Send + 'static>(data: R) -> MediaBuilder { |
||||
MediaBuilder { |
||||
data: MediaBuilderData::Reader(Box::from(data)), |
||||
filename: None, |
||||
mimetype: None, |
||||
description: None, |
||||
focus: None, |
||||
} |
||||
} |
||||
|
||||
/// Create a new MediaBuilder from a file under `path`
|
||||
///
|
||||
/// This function will not check whether the file exists or if it can be read. If the path is
|
||||
/// not valid, [`add_media()`](trait.MastodonClient.html#method.add_media) will return an error when called with the `MediaBuilder`.
|
||||
pub fn from_file(path: impl AsRef<Path>) -> MediaBuilder { |
||||
let pb = path.as_ref().to_owned(); |
||||
let filename = pb |
||||
.file_name() |
||||
.expect("file name") |
||||
.to_string_lossy() |
||||
.to_string(); |
||||
let mimetype = match pb.extension().map(|s| s.to_str()).flatten() { |
||||
Some("jpg") | Some("jpeg") => Some("image/jpeg".to_string()), |
||||
Some("png") => Some("image/png".to_string()), |
||||
Some("gif") => Some("image/gif".to_string()), |
||||
Some("txt") => Some("text/plain".to_string()), |
||||
// ...
|
||||
_ => None, |
||||
}; |
||||
|
||||
MediaBuilder { |
||||
file, |
||||
data: MediaBuilderData::File(pb), |
||||
filename: Some(filename), |
||||
mimetype, |
||||
description: None, |
||||
focus: None, |
||||
} |
||||
} |
||||
|
||||
/// Set filename
|
||||
pub fn filename(&mut self, filename: impl ToString) { |
||||
self.filename = Some(filename.to_string()); |
||||
} |
||||
|
||||
/// Set custom mime type
|
||||
pub fn mimetype(&mut self, mimetype: impl ToString) { |
||||
self.mimetype = Some(mimetype.to_string()); |
||||
} |
||||
|
||||
/// Set an alt text description for the attachment.
|
||||
pub fn description(mut self, description: Cow<'static, str>) -> Self { |
||||
self.description = Some(description); |
||||
self |
||||
pub fn description(&mut self, description: impl ToString) { |
||||
self.description = Some(description.to_string()); |
||||
} |
||||
|
||||
/// Set a focus point for an image attachment.
|
||||
pub fn focus(mut self, f1: f32, f2: f32) -> Self { |
||||
pub fn focus(&mut self, f1: f64, f2: f64) { |
||||
self.focus = Some((f1, f2)); |
||||
self |
||||
} |
||||
} |
||||
|
||||
// Convenience helper so that the mastodon.media() method can be called with a
|
||||
// file name only (owned string).
|
||||
impl From<String> for MediaBuilder { |
||||
fn from(file: String) -> MediaBuilder { |
||||
MediaBuilder { |
||||
file: file.into(), |
||||
description: None, |
||||
focus: None, |
||||
} |
||||
} |
||||
} |
||||
#[cfg(test)] |
||||
mod tests { |
||||
use super::*; |
||||
use std::io::Cursor; |
||||
|
||||
// Convenience helper so that the mastodon.media() method can be called with a
|
||||
// file name only (borrowed string).
|
||||
impl From<&'static str> for MediaBuilder { |
||||
fn from(file: &'static str) -> MediaBuilder { |
||||
MediaBuilder { |
||||
file: file.into(), |
||||
description: None, |
||||
focus: None, |
||||
#[test] |
||||
fn test_from_reader() { |
||||
let source = vec![0u8, 1, 2]; |
||||
let builder = MediaBuilder::from_reader(Cursor::new(source.clone())); |
||||
|
||||
assert_eq!(builder.filename, None); |
||||
assert_eq!(builder.mimetype, None); |
||||
assert_eq!(builder.description, None); |
||||
assert_eq!(builder.focus, None); |
||||
|
||||
if let MediaBuilderData::Reader(r) = builder.data { |
||||
assert_eq!(r.bytes().map(|b| b.unwrap()).collect::<Vec<u8>>(), source); |
||||
} else { |
||||
panic!("Unable to destructure MediaBuilder.data into a reader"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Convenience helper so that the mastodon.media() method can be called with a
|
||||
// file name only (Cow string).
|
||||
impl From<Cow<'static, str>> for MediaBuilder { |
||||
fn from(file: Cow<'static, str>) -> MediaBuilder { |
||||
MediaBuilder { |
||||
file, |
||||
description: None, |
||||
focus: None, |
||||
#[test] |
||||
fn test_from_file() { |
||||
let builder = MediaBuilder::from_file("/fake/file/path.png".into()); |
||||
|
||||
assert_eq!(builder.filename, None); |
||||
assert_eq!(builder.mimetype, None); |
||||
assert_eq!(builder.description, None); |
||||
assert_eq!(builder.focus, None); |
||||
|
||||
if let MediaBuilderData::File(f) = builder.data { |
||||
assert_eq!(f, "/fake/file/path.png"); |
||||
} else { |
||||
panic!("Unable to destructure MediaBuilder.data into a filepath"); |
||||
} |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue