ibm_watson/text-to-speech/customisations/models/
mod.rs

1use std::borrow::Cow;
2
3use reqwest::{Method, Request, StatusCode, Url, Version};
4use serde::{Deserialize, Serialize};
5
6use crate::tts::TextToSpeech;
7
8use super::{
9    errors::{CreateModelError, DeleteModelError, GetModelError, ListModelError, UpdateModelError},
10    prompts::Prompt,
11    words::Word,
12};
13
14#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
15/// Defines a custom model
16pub struct Model {
17    /// the customisation id (guid) of the custom model. the create a custom model method returns only this field. it does not not return the other fields of this object.
18    #[serde(rename = "customization_id")]
19    pub customisation_id: String,
20    /// the name of the custom model.
21    #[serde(rename = "name")]
22    pub name: String,
23    /// the language identifier of the custom model (for example, en-us).
24    #[serde(rename = "language", skip_serializing_if = "Option::is_none")]
25    pub language: Option<String>,
26    /// the guid of the credentials for the instance of the service that owns the custom model.
27    #[serde(rename = "owner", skip_serializing_if = "Option::is_none")]
28    pub owner: Option<String>,
29    /// the date and time in coordinated universal time (utc) at which the custom model was created. the value is provided in full iso 8601 format (yyyy-mm-ddthh:mm:ss.stzd)
30    #[serde(rename = "created", skip_serializing_if = "Option::is_none")]
31    pub created: Option<String>,
32    /// the date and time in coordinated universal time (utc) at which the custom model was last modified. the created and updated fields are equal when a model is first added but has yet to be updated. the value is provided in full iso 8601 format (yyyy-mm-ddthh:mm:ss.stzd).
33    #[serde(rename = "last_modified", skip_serializing_if = "Option::is_none")]
34    pub last_modified: Option<String>,
35    /// the description of the custom model.
36    #[serde(rename = "description", skip_serializing_if = "Option::is_none")]
37    pub description: Option<String>,
38    /// an array of word objects that lists the words and their translations from the custom model. the words are listed in alphabetical order, with uppercase letters listed before lowercase letters. the array is empty if no words are defined for the custom model. this field is returned only by the get a custom model method.
39    #[serde(rename = "words", skip_serializing_if = "Option::is_none")]
40    pub words: Option<Vec<Word>>,
41    /// an array of prompt objects that provides information about the prompts that are defined for the specified custom model. the array is empty if no prompts are defined for the custom model. this field is returned only by the get a custom model method.
42    #[serde(rename = "prompts", skip_serializing_if = "Option::is_none")]
43    pub prompts: Option<Vec<Prompt>>,
44}
45
46#[non_exhaustive]
47#[derive(Default)]
48/// The language of the new custom model
49pub enum Language {
50    /// Arabic
51    ArMs,
52    /// Czech (Czechia)
53    CsCz,
54    /// German (Germany)
55    DeDe,
56    /// English (Australia)
57    EnAu,
58    /// English (United Kingdom)
59    EnGb,
60    #[default]
61    /// English (United States)
62    EnUs,
63    /// Spanish (Spain)
64    EsEs,
65    /// Spanish (Latin America)
66    EsLa,
67    /// Spanish (United States)
68    EsUs,
69    /// French (Canada)
70    FrCa,
71    /// French (France)
72    FrFr,
73    /// Italian (Italy)
74    ItIt,
75    /// Japanese (Japan)
76    JaJp,
77    /// Koren (South Korea)
78    KoKr,
79    /// Dutch (Belgium)
80    NlBe,
81    /// Dutch (Netherlands)
82    NlNl,
83    /// Portuguese (Brazil)
84    PtBr,
85    /// Swedish (Sweden)
86    SvSe,
87    /// Chinese (PRC)
88    ZhCn,
89}
90
91impl Language {
92    /// The value that the server expects for a particular language
93    pub fn id(&self) -> Cow<'static, str> {
94        match self {
95            Language::ArMs => Cow::from("ar-MS"),
96            Language::CsCz => Cow::from("cs-CZ"),
97            Language::DeDe => Cow::from("de-DE"),
98            Language::EnAu => Cow::from("en-AU"),
99            Language::EnGb => Cow::from("en-GB"),
100            Language::EnUs => Cow::from("en-US"),
101            Language::EsEs => Cow::from("es-ES"),
102            Language::EsLa => Cow::from("es-LA"),
103            Language::EsUs => Cow::from("es-US"),
104            Language::FrCa => Cow::from("fr-CA"),
105            Language::FrFr => Cow::from("fr-FR"),
106            Language::ItIt => Cow::from("it-IT"),
107            Language::JaJp => Cow::from("ja-JP"),
108            Language::KoKr => Cow::from("ko-KR"),
109            Language::NlBe => Cow::from("nl-BE"),
110            Language::NlNl => Cow::from("nl-NL"),
111            Language::PtBr => Cow::from("pt-BR"),
112            Language::SvSe => Cow::from("sv-SE"),
113            Language::ZhCn => Cow::from("zh-CN"),
114        }
115    }
116}
117
118impl TextToSpeech<'_> {
119    /// Creates a new empty custom model. You must specify a name for the new custom model. You can optionally specify the language and a description for the new model. The model is owned by the instance of the service whose credentials are used to create it
120    ///
121    /// # Parameters
122    ///
123    /// * `name` - The name of the new custom model
124    /// * `language` - The language of the new custom model. You create a custom model for a specific language, not for a specific voice. A custom model can be used with any voice for its specified language. If [`None`] is specified, the [`default language`] is used
125    /// * `description` - A description of the new custom model. Specifying a description is recommended
126    ///
127    /// # Example
128    /// ``` no_run
129    /// # use ibm_watson::{
130    /// #     auth::IamAuthenticator,
131    /// #     tts::{voices::WatsonVoice, TextToSpeech},
132    /// # };
133    /// # async fn foo()-> Result<(), Box<dyn std::error::Error>> {
134    /// # let auth = IamAuthenticator::new("api_key").await?;
135    /// # let tts = TextToSpeech::new(&auth, "service_url");
136    /// let model = tts.create_custom_model("new model", None, Some("example")).await?;
137    /// println!("model: {:#?}", model);
138    /// # Ok(())
139    /// # }
140    /// ```
141    /// [`None`]: std::option::Option::None
142    /// [`default language`]: self::Language::EnUs
143    pub async fn create_custom_model(
144        &self,
145        name: impl AsRef<str>,
146        language: Option<Language>,
147        description: Option<impl AsRef<str>>,
148    ) -> Result<Model, CreateModelError> {
149        let mut url = Url::parse(self.service_url).unwrap();
150        url.set_path("v1/customizations");
151        #[derive(Serialize, Deserialize)]
152        struct FormBody<'a> {
153            name: &'a str,
154            language: &'a str,
155            description: &'a str,
156        }
157        let name = name.as_ref();
158        let language = language.unwrap_or_default().id().to_owned();
159        let description = match description {
160            Some(s) => s.as_ref().to_owned(),
161            None => String::default(),
162        };
163        let form_body = FormBody {
164            name,
165            language: &language,
166            description: &description,
167        };
168        let client = self.get_client();
169        let response = client
170            .post(url)
171            .json(&form_body)
172            .version(if cfg!(feature = "http2") {
173                Version::HTTP_2
174            } else {
175                Version::default()
176            })
177            .send()
178            .await
179            .map_err(|e| CreateModelError::ConnectionError(e.to_string()))?;
180        match response.status() {
181            StatusCode::OK => {
182                let root: Model = response.json().await.unwrap();
183                Ok(root)
184            }
185            StatusCode::BAD_REQUEST => Err(CreateModelError::BadRequest400),
186            StatusCode::INTERNAL_SERVER_ERROR => Err(CreateModelError::InternalServerError500),
187            StatusCode::SERVICE_UNAVAILABLE => Err(CreateModelError::ServiceUnavailable503),
188            _ => {
189                unreachable!()
190            }
191        }
192    }
193
194    /// Lists metadata such as the name and description for all custom models that are owned by an instance of the service. Specify a [`language`] to list the custom models for that language only. To see the words and prompts in addition to the metadata for a specific custom model, use [`get_custom_model()`]. You must use credentials for the instance of the service that owns a model to list information about it.
195    ///
196    /// # Parameters
197    ///
198    /// * `language` - The language for which custom models that are owned by the requesting credentials are to be returned. Pass [`None`] to see all custom models that are owned by the requester
199    ///
200    /// # Example
201    /// ``` no_run
202    /// # use ibm_watson::{
203    /// #     auth::IamAuthenticator,
204    /// #     tts::{voices::WatsonVoice, TextToSpeech},
205    /// # };
206    /// # async fn foo()-> Result<(), Box<dyn std::error::Error>> {
207    /// # let auth = IamAuthenticator::new("api_key").await?;
208    /// # let tts = TextToSpeech::new(&auth, "service_url");
209    /// let models = tts.list_custom_models(None).await?;
210    /// println!("found: {:#?} models", models.len());
211    /// # Ok(())
212    /// # }
213    /// ```
214    /// [`None`]: std::option::Option::None
215    /// [`language`]: self::Language
216    /// [`get_custom_model()`]: Self::get_custom_model()
217    pub async fn list_custom_models(
218        &self,
219        language: Option<Language>,
220    ) -> Result<Vec<Model>, ListModelError> {
221        let mut url = Url::parse(self.service_url).unwrap();
222        url.set_path("v1/customizations");
223        url.set_query(Some(&language.unwrap_or_default().id()));
224        let mut req = Request::new(Method::GET, url);
225
226        if cfg!(feature = "http2") {
227            *req.version_mut() = Version::HTTP_2;
228        }
229
230        let client = self.get_client();
231        let response = client
232            .execute(req)
233            .await
234            .map_err(|e| ListModelError::ConnectionError(e.to_string()))?;
235        match response.status() {
236            StatusCode::OK => {
237                #[derive(Deserialize, Serialize)]
238                struct Root {
239                    customizations: Vec<Model>,
240                }
241                let root: Root = response.json().await.unwrap();
242                Ok(root.customizations)
243            }
244            StatusCode::BAD_REQUEST => Err(ListModelError::BadRequest400),
245            StatusCode::INTERNAL_SERVER_ERROR => Err(ListModelError::InternalServerError500),
246            StatusCode::SERVICE_UNAVAILABLE => Err(ListModelError::ServiceUnavailable503),
247            _ => {
248                unreachable!()
249            }
250        }
251    }
252
253    /// Updates information for the specified custom model. You can update metadata such as the
254    /// name and description of the model. You can also update the words in the model and their
255    /// translations. Adding a new translation for a word that already exists in a custom model
256    /// overwrites the word's existing translation. A custom model can contain no more than 20,000
257    /// entries. You must use credentials for the instance of the service that owns a model to
258    /// update it
259    ///
260    /// # Parameters
261    ///
262    /// * `customisation_id` - The customisation ID (GUID) of the custom model. You must make the request with credentials for the instance of the service that owns the custom model
263    /// * `name` - A new [`name`] for the custom model
264    /// * `description` - A new [`description`] for the custom model
265    /// * `words` - An array of [`Word`] objects that provides the words and their translations that are to be added or updated for the custom model. Pass an empty array to make no additions or updates
266    ///
267    /// # Example
268    /// ``` no_run
269    /// # use ibm_watson::{
270    /// #     auth::IamAuthenticator,
271    /// #     tts::{voices::WatsonVoice, TextToSpeech},
272    /// # };
273    /// # async fn foo()-> Result<(), Box<dyn std::error::Error>> {
274    /// # let auth = IamAuthenticator::new("api_key").await?;
275    /// # let tts = TextToSpeech::new(&auth, "service_url");
276    /// tts.update_custom_model("cust-id", Some("foo"), None, None).await?;
277    /// # Ok(())
278    /// # }
279    /// ```
280    /// [`name`]: crate::tts::customisations::Model::name
281    /// [`description`]: crate::tts::customisations::Model::description
282    /// [`Word`]: crate::tts::customisations::Word
283    pub async fn update_custom_model(
284        &self,
285        customisation_id: impl AsRef<str>,
286        name: Option<&str>,
287        description: Option<&str>,
288        words: Option<&[Word]>,
289    ) -> Result<(), UpdateModelError> {
290        let mut url = Url::parse(self.service_url).unwrap();
291        url.set_path(&format!("v1/customizations/{}", customisation_id.as_ref()));
292        #[derive(Deserialize, Serialize)]
293        struct Foo<'a> {
294            #[serde(skip_serializing_if = "Option::is_none")]
295            name: Option<&'a str>,
296            #[serde(skip_serializing_if = "Option::is_none")]
297            description: Option<&'a str>,
298            #[serde(skip_serializing_if = "Option::is_none")]
299            words: Option<Vec<Word>>,
300        }
301        impl<'a> Foo<'a> {
302            fn new(
303                name: Option<&'a str>,
304                description: Option<&'a str>,
305                words: Option<&'a [Word]>,
306            ) -> Self {
307                Self {
308                    name,
309                    description,
310                    words: words.map(|f| f.to_owned()),
311                }
312            }
313        }
314        let data = Foo::new(name, description, words);
315        let client = self.get_client();
316        let response = client
317            .post(url)
318            .json(&data)
319            .version(if cfg!(feature = "http2") {
320                Version::HTTP_2
321            } else {
322                Version::default()
323            })
324            .send()
325            .await
326            .map_err(|e| UpdateModelError::ConnectionError(e.to_string()))?;
327        match response.status() {
328            StatusCode::OK => Ok(()),
329            StatusCode::BAD_REQUEST => Err(UpdateModelError::BadRequest400),
330            StatusCode::INTERNAL_SERVER_ERROR => Err(UpdateModelError::InternalServerError500),
331            StatusCode::SERVICE_UNAVAILABLE => Err(UpdateModelError::ServiceUnavailable503),
332            StatusCode::UNAUTHORIZED => Err(UpdateModelError::Unauthorised401(
333                customisation_id.as_ref().to_owned(),
334            )),
335            _ => {
336                unreachable!()
337            }
338        }
339    }
340
341    /// Gets all information about a specified custom model. In addition to metadata such as the name and description of the custom model, the output includes the words and their translations that are defined for the model, as well as any prompts that are defined for the model. To see just the metadata for a model, use [`list_custom_models()`].
342    ///
343    /// # Parameters
344    ///
345    /// * `customisation_id` - The customisation ID (GUID) of the custom model. You must make the request with credentials for the instance of the service that owns the custom model
346    ///
347    /// # Example
348    /// ``` no_run
349    /// # use ibm_watson::{
350    /// #     auth::IamAuthenticator,
351    /// #     tts::{voices::WatsonVoice, TextToSpeech},
352    /// # };
353    /// # async fn foo()-> Result<(), Box<dyn std::error::Error>> {
354    /// # let auth = IamAuthenticator::new("api_key").await?;
355    /// # let tts = TextToSpeech::new(&auth, "service_url");
356    /// let model = tts.get_custom_model("cust-id").await?;
357    /// println!("{:#?}", model);
358    /// # Ok(())
359    /// # }
360    /// ```
361    /// [`language`]: self::Language
362    /// [`list_custom_models()`]: Self::list_custom_models()
363    pub async fn get_custom_model(
364        &self,
365        customisation_id: impl AsRef<str>,
366    ) -> Result<Model, GetModelError> {
367        let mut url = Url::parse(self.service_url).unwrap();
368        url.set_path(&format!("v1/customizations/{}", customisation_id.as_ref()));
369        let mut req = Request::new(Method::GET, url);
370
371        if cfg!(feature = "http2") {
372            *req.version_mut() = Version::HTTP_2;
373        }
374
375        let client = self.get_client();
376        let response = client
377            .execute(req)
378            .await
379            .map_err(|e| GetModelError::ConnectionError(e.to_string()))?;
380        match response.status() {
381            StatusCode::OK => {
382                let root: Model = response.json().await.unwrap();
383                Ok(root)
384            }
385            StatusCode::BAD_REQUEST => Err(GetModelError::BadRequest400(
386                customisation_id.as_ref().to_owned(),
387            )),
388            StatusCode::INTERNAL_SERVER_ERROR => Err(GetModelError::InternalServerError500),
389            StatusCode::SERVICE_UNAVAILABLE => Err(GetModelError::ServiceUnavailable503),
390            StatusCode::NOT_MODIFIED => Err(GetModelError::NotModified304),
391            StatusCode::UNAUTHORIZED => Err(GetModelError::Unauthorised401(
392                customisation_id.as_ref().to_owned(),
393            )),
394            _ => {
395                unreachable!()
396            }
397        }
398    }
399
400    /// Deletes the specified custom model. You must use credentials for the instance of the service that owns a model to delete it.
401    ///
402    /// # Parameters
403    ///
404    /// * `customisation_id` - The customisation ID (GUID) of the custom model. You must make the request with credentials for the instance of the service that owns the custom model
405    ///
406    /// # Example
407    /// ``` no_run
408    /// # use ibm_watson::{
409    /// #     auth::IamAuthenticator,
410    /// #     tts::{voices::WatsonVoice, TextToSpeech},
411    /// # };
412    /// # async fn foo()-> Result<(), Box<dyn std::error::Error>> {
413    /// # let auth = IamAuthenticator::new("api_key").await?;
414    /// # let tts = TextToSpeech::new(&auth, "service_url");
415    /// if tts.delete_custom_model("cust-id").await.is_ok() {
416    ///     println!("model deleted");
417    /// }
418    /// # Ok(())
419    /// # }
420    /// ```
421    /// [`language`]: self::Language
422    /// [`list_custom_models()`]: Self::list_custom_models()
423    pub async fn delete_custom_model(
424        &self,
425        customisation_id: impl AsRef<str>,
426    ) -> Result<(), DeleteModelError> {
427        let mut url = Url::parse(self.service_url).unwrap();
428        url.set_path(&format!("v1/customizations/{}", customisation_id.as_ref()));
429        let mut req = Request::new(Method::DELETE, url);
430
431        if cfg!(feature = "http2") {
432            *req.version_mut() = Version::HTTP_2;
433        }
434
435        let client = self.get_client();
436        let response = client
437            .execute(req)
438            .await
439            .map_err(|e| DeleteModelError::ConnectionError(e.to_string()))?;
440        match response.status() {
441            StatusCode::NO_CONTENT => Ok(()),
442            StatusCode::BAD_REQUEST => Err(DeleteModelError::BadRequest400(
443                customisation_id.as_ref().to_owned(),
444            )),
445            StatusCode::INTERNAL_SERVER_ERROR => Err(DeleteModelError::InternalServerError500),
446            StatusCode::SERVICE_UNAVAILABLE => Err(DeleteModelError::ServiceUnavailable503),
447            StatusCode::UNAUTHORIZED => Err(DeleteModelError::Unauthorised401(
448                customisation_id.as_ref().to_owned(),
449            )),
450            _ => {
451                unreachable!()
452            }
453        }
454    }
455}