Skip to main content

systemprompt_provider_contracts/documents/
format.rs

1//! The document formats the store understands.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use std::fmt;
7use std::str::FromStr;
8
9use schemars::JsonSchema;
10use serde::{Deserialize, Serialize};
11
12#[derive(
13    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
14)]
15#[serde(rename_all = "snake_case")]
16pub enum DocumentFormat {
17    Docx,
18    Xlsx,
19    Pptx,
20    Odt,
21    Ods,
22    Odp,
23    Html,
24    Markdown,
25    Text,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
29#[error("unknown document format `{value}`")]
30pub struct UnknownDocumentFormat {
31    pub value: String,
32}
33
34impl DocumentFormat {
35    pub const ALL: [Self; 9] = [
36        Self::Docx,
37        Self::Xlsx,
38        Self::Pptx,
39        Self::Odt,
40        Self::Ods,
41        Self::Odp,
42        Self::Html,
43        Self::Markdown,
44        Self::Text,
45    ];
46
47    #[must_use]
48    pub const fn as_str(self) -> &'static str {
49        match self {
50            Self::Docx => "docx",
51            Self::Xlsx => "xlsx",
52            Self::Pptx => "pptx",
53            Self::Odt => "odt",
54            Self::Ods => "ods",
55            Self::Odp => "odp",
56            Self::Html => "html",
57            Self::Markdown => "markdown",
58            Self::Text => "text",
59        }
60    }
61
62    #[must_use]
63    pub const fn mime_type(self) -> &'static str {
64        match self {
65            Self::Docx => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
66            Self::Xlsx => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
67            Self::Pptx => {
68                "application/vnd.openxmlformats-officedocument.presentationml.presentation"
69            },
70            Self::Odt => "application/vnd.oasis.opendocument.text",
71            Self::Ods => "application/vnd.oasis.opendocument.spreadsheet",
72            Self::Odp => "application/vnd.oasis.opendocument.presentation",
73            Self::Html => "text/html",
74            Self::Markdown => "text/markdown",
75            Self::Text => "text/plain",
76        }
77    }
78
79    #[must_use]
80    pub const fn extension(self) -> &'static str {
81        match self {
82            Self::Markdown => "md",
83            Self::Text => "txt",
84            other => other.as_str(),
85        }
86    }
87
88    #[must_use]
89    pub fn from_extension(extension: &str) -> Option<Self> {
90        let lower = extension.trim_start_matches('.').to_ascii_lowercase();
91        match lower.as_str() {
92            "htm" => Some(Self::Html),
93            "md" | "markdown" => Some(Self::Markdown),
94            "txt" | "text" => Some(Self::Text),
95            other => Self::ALL.into_iter().find(|f| f.as_str() == other),
96        }
97    }
98
99    #[must_use]
100    pub const fn is_office(self) -> bool {
101        matches!(
102            self,
103            Self::Docx | Self::Xlsx | Self::Pptx | Self::Odt | Self::Ods | Self::Odp
104        )
105    }
106
107    #[must_use]
108    pub const fn supports_three_way_merge(self) -> bool {
109        matches!(self, Self::Html | Self::Markdown | Self::Text)
110    }
111}
112
113impl fmt::Display for DocumentFormat {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        f.write_str(self.as_str())
116    }
117}
118
119impl FromStr for DocumentFormat {
120    type Err = UnknownDocumentFormat;
121
122    fn from_str(s: &str) -> Result<Self, Self::Err> {
123        Self::ALL
124            .into_iter()
125            .find(|f| f.as_str() == s)
126            .ok_or_else(|| UnknownDocumentFormat {
127                value: s.to_owned(),
128            })
129    }
130}