parent
5a22c30370
commit
dd1a68f714
@ -1,72 +1,148 @@ |
|||||||
use serde::Serialize; |
use std::fmt; |
||||||
use std::borrow::Cow; |
use std::io::Read; |
||||||
|
use std::path::{Path, PathBuf}; |
||||||
|
|
||||||
/// A builder pattern struct for constructing a media attachment.
|
#[derive(Debug)] |
||||||
#[derive(Debug, Default, Clone, Serialize)] |
/// 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 { |
pub struct MediaBuilder { |
||||||
/// The file name of the attachment to be uploaded.
|
/// The media attachment itself
|
||||||
pub file: Cow<'static, str>, |
pub data: MediaBuilderData, |
||||||
/// The alt text of the attachment.
|
|
||||||
pub description: Option<Cow<'static, str>>, |
|
||||||
/// The focus point for images.
|
|
||||||
pub focus: Option<(f32, f32)>, |
|
||||||
} |
|
||||||
|
|
||||||
impl MediaBuilder { |
/// The filename to send to the server
|
||||||
/// Create a new attachment from a file name.
|
pub filename: Option<String>, |
||||||
pub fn new(file: Cow<'static, str>) -> Self { |
|
||||||
MediaBuilder { |
/// Mimetype to send to the server, identifying what is in the attachment.
|
||||||
file, |
///
|
||||||
description: None, |
/// The string should be a valid mimetype.
|
||||||
focus: None, |
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)>, |
||||||
} |
} |
||||||
|
|
||||||
/// Set an alt text description for the attachment.
|
/// Enum representing possible sources of attachments to upload
|
||||||
pub fn description(mut self, description: Cow<'static, str>) -> Self { |
pub enum MediaBuilderData { |
||||||
self.description = Some(description); |
/// An arbitrary reader. It is useful for reading from media already in memory.
|
||||||
self |
Reader(Box<dyn Read + Send>), |
||||||
|
|
||||||
|
/// Variant represening a file path of the file to attach.
|
||||||
|
File(PathBuf), |
||||||
} |
} |
||||||
|
|
||||||
/// Set a focus point for an image attachment.
|
impl fmt::Debug for MediaBuilderData { |
||||||
pub fn focus(mut self, f1: f32, f2: f32) -> Self { |
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
||||||
self.focus = Some((f1, f2)); |
match self { |
||||||
self |
MediaBuilderData::File(f) => fmt.debug_tuple("File").field(&f).finish(), |
||||||
|
MediaBuilderData::Reader(_) => fmt |
||||||
|
.debug_tuple("Reader") |
||||||
|
.field(&format_args!("...")) |
||||||
|
.finish(), |
||||||
|
} |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
// Convenience helper so that the mastodon.media() method can be called with a
|
impl MediaBuilder { |
||||||
// file name only (owned string).
|
/// Create a new MediaBuilder from a reader `data`
|
||||||
impl From<String> for MediaBuilder { |
pub fn from_reader<R: Read + Send + 'static>(data: R) -> MediaBuilder { |
||||||
fn from(file: String) -> MediaBuilder { |
|
||||||
MediaBuilder { |
MediaBuilder { |
||||||
file: file.into(), |
data: MediaBuilderData::Reader(Box::from(data)), |
||||||
|
filename: None, |
||||||
|
mimetype: None, |
||||||
description: None, |
description: None, |
||||||
focus: None, |
focus: None, |
||||||
} |
} |
||||||
} |
} |
||||||
} |
|
||||||
|
|
||||||
// Convenience helper so that the mastodon.media() method can be called with a
|
/// Create a new MediaBuilder from a file under `path`
|
||||||
// file name only (borrowed string).
|
///
|
||||||
impl From<&'static str> for MediaBuilder { |
/// This function will not check whether the file exists or if it can be read. If the path is
|
||||||
fn from(file: &'static str) -> MediaBuilder { |
/// 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 { |
MediaBuilder { |
||||||
file: file.into(), |
data: MediaBuilderData::File(pb), |
||||||
|
filename: Some(filename), |
||||||
|
mimetype, |
||||||
description: None, |
description: None, |
||||||
focus: None, |
focus: None, |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
|
/// Set filename
|
||||||
|
pub fn filename(&mut self, filename: impl ToString) { |
||||||
|
self.filename = Some(filename.to_string()); |
||||||
} |
} |
||||||
|
|
||||||
// Convenience helper so that the mastodon.media() method can be called with a
|
/// Set custom mime type
|
||||||
// file name only (Cow string).
|
pub fn mimetype(&mut self, mimetype: impl ToString) { |
||||||
impl From<Cow<'static, str>> for MediaBuilder { |
self.mimetype = Some(mimetype.to_string()); |
||||||
fn from(file: Cow<'static, str>) -> MediaBuilder { |
} |
||||||
MediaBuilder { |
|
||||||
file, |
/// Set an alt text description for the attachment.
|
||||||
description: None, |
pub fn description(&mut self, description: impl ToString) { |
||||||
focus: None, |
self.description = Some(description.to_string()); |
||||||
|
} |
||||||
|
|
||||||
|
/// Set a focus point for an image attachment.
|
||||||
|
pub fn focus(&mut self, f1: f64, f2: f64) { |
||||||
|
self.focus = Some((f1, f2)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests { |
||||||
|
use super::*; |
||||||
|
use std::io::Cursor; |
||||||
|
|
||||||
|
#[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"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[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