noosphere_cli/native/
extension.rs

1//! Helpers for dealing with translation between slugs and files
2
3use std::str::FromStr;
4
5use anyhow::Result;
6use noosphere_core::data::{ContentType, Header, MemoIpld};
7
8/// Given a [MemoIpld], attempt to infer a file extension. A 'File-Extension' header
9/// will be used (if present); otherwise, the extension will be inferred from
10/// the 'Content-Type' header (again, if present).
11pub fn infer_file_extension(memo: &MemoIpld) -> Result<Option<String>> {
12    if let Some(extension) = memo.get_first_header(&Header::FileExtension) {
13        return Ok(Some(extension));
14    }
15
16    Ok(match memo.content_type() {
17        Some(content_type) => match content_type {
18            ContentType::Subtext => Some("subtext".into()),
19            ContentType::Sphere => Some("sphere".into()),
20            ContentType::Bytes => None,
21            ContentType::Unknown(content_type) => {
22                match mime_guess::get_mime_extensions_str(&content_type) {
23                    Some(extensions) => extensions.first().map(|str| String::from(*str)),
24                    None => None,
25                }
26            }
27            ContentType::Cbor => Some("json".into()),
28            ContentType::Json => Some("cbor".into()),
29            ContentType::Text => Some("txt".into()),
30        },
31        None => {
32            warn!("No content type specified; cannot infer a file extension");
33            None
34        }
35    })
36}
37
38/// Given a file extension, infer its mime
39pub async fn infer_content_type(extension: &str) -> Result<ContentType> {
40    // TODO(#558): User-specified/customized extension->mime mapping
41    Ok(match extension {
42        "subtext" => ContentType::Subtext,
43        "sphere" => ContentType::Sphere,
44        _ => ContentType::from_str(
45            mime_guess::from_ext(extension)
46                .first_raw()
47                .unwrap_or("raw/bytes"),
48        )?,
49    })
50}