2017-04-29 21:14:34 +00:00
// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
2024-06-29 14:14:12 +00:00
use xso ::error ::{ Error , FromElementError } ;
2024-06-30 09:25:45 +00:00
use crate ::data_forms_validate ::Validate ;
2018-12-18 14:27:30 +00:00
use crate ::media_element ::MediaElement ;
2018-12-18 14:32:05 +00:00
use crate ::ns ;
2024-07-24 18:28:22 +00:00
use minidom ::Element ;
2017-04-18 19:44:36 +00:00
2018-05-28 15:05:04 +00:00
generate_element! (
2018-08-08 17:35:32 +00:00
/// Represents one of the possible values for a list- field.
2018-05-28 15:05:04 +00:00
Option_ , " option " , DATA_FORMS ,
attributes : [
2018-08-08 17:35:32 +00:00
/// The optional label to be displayed to the user for this option.
2019-02-24 19:26:40 +00:00
label : Option < String > = " label "
2018-05-28 15:05:04 +00:00
] ,
children : [
2018-08-08 17:35:32 +00:00
/// The value returned to the server when selecting this option.
2018-05-28 15:05:04 +00:00
value : Required < String > = ( " value " , DATA_FORMS ) = > String
]
) ;
2017-05-25 00:00:17 +00:00
2018-08-08 17:35:32 +00:00
generate_attribute! (
/// The type of a [field](struct.Field.html) element.
FieldType , " type " , {
/// This field can only take the values "0" or "false" for a false
/// value, and "1" or "true" for a true value.
Boolean = > " boolean " ,
/// This field describes data, it must not be sent back to the
/// requester.
Fixed = > " fixed " ,
/// This field is hidden, it should not be displayed to the user but
/// should be sent back to the requester.
Hidden = > " hidden " ,
/// This field accepts one or more [JIDs](../../jid/struct.Jid.html).
/// A client may want to let the user autocomplete them based on their
/// contacts list for instance.
JidMulti = > " jid-multi " ,
/// This field accepts one [JID](../../jid/struct.Jid.html). A client
/// may want to let the user autocomplete it based on their contacts
/// list for instance.
JidSingle = > " jid-single " ,
/// This field accepts one or more values from the list provided as
/// [options](struct.Option_.html).
ListMulti = > " list-multi " ,
/// This field accepts one value from the list provided as
/// [options](struct.Option_.html).
ListSingle = > " list-single " ,
2017-10-10 17:26:05 +00:00
2018-08-08 17:35:32 +00:00
/// This field accepts one or more free form text lines.
TextMulti = > " text-multi " ,
/// This field accepts one free form password, a client should hide it
/// in its user interface.
TextPrivate = > " text-private " ,
/// This field accepts one free form text line.
TextSingle = > " text-single " ,
} , Default = TextSingle
) ;
/// Represents a field in a [data form](struct.DataForm.html).
2020-11-29 20:17:51 +00:00
#[ derive(Debug, Clone, PartialEq) ]
2017-04-18 19:44:36 +00:00
pub struct Field {
2018-08-08 17:35:32 +00:00
/// The unique identifier for this field, in the form.
2023-08-30 13:55:18 +00:00
pub var : Option < String > ,
2018-08-08 17:35:32 +00:00
/// The type of this field.
2017-05-21 16:08:05 +00:00
pub type_ : FieldType ,
2018-08-08 17:35:32 +00:00
/// The label to be possibly displayed to the user for this field.
2017-04-18 19:44:36 +00:00
pub label : Option < String > ,
2018-08-08 17:35:32 +00:00
/// The form will be rejected if this field isn’ t present.
2017-05-21 15:41:29 +00:00
pub required : bool ,
2018-08-08 17:35:32 +00:00
2023-08-30 15:01:17 +00:00
/// The natural-language description of the field, intended for presentation in a user-agent
pub desc : Option < String > ,
2018-08-08 17:35:32 +00:00
/// A list of allowed values.
2017-05-21 15:41:29 +00:00
pub options : Vec < Option_ > ,
2018-08-08 17:35:32 +00:00
/// The values provided for this field.
2017-04-18 19:44:36 +00:00
pub values : Vec < String > ,
2018-08-08 17:35:32 +00:00
/// A list of media related to this field.
2017-04-18 19:44:36 +00:00
pub media : Vec < MediaElement > ,
2024-06-19 17:15:34 +00:00
/// Validation rules for this field.
pub validate : Option < Validate > ,
2017-04-18 19:44:36 +00:00
}
2017-07-29 03:19:58 +00:00
impl Field {
2022-01-03 10:50:58 +00:00
/// Create a new Field, of the given var and type.
pub fn new ( var : & str , type_ : FieldType ) -> Field {
Field {
2023-08-30 13:55:18 +00:00
var : Some ( String ::from ( var ) ) ,
2022-01-03 10:50:58 +00:00
type_ ,
label : None ,
required : false ,
2023-08-30 15:01:17 +00:00
desc : None ,
2022-01-03 10:50:58 +00:00
options : Vec ::new ( ) ,
media : Vec ::new ( ) ,
values : Vec ::new ( ) ,
2024-06-19 17:15:34 +00:00
validate : None ,
2022-01-03 10:50:58 +00:00
}
}
/// Set only one value in this Field.
pub fn with_value ( mut self , value : & str ) -> Field {
self . values . push ( String ::from ( value ) ) ;
self
}
/// Create a text-single Field with the given var and unique value.
pub fn text_single ( var : & str , value : & str ) -> Field {
Field ::new ( var , FieldType ::TextSingle ) . with_value ( value )
}
2017-07-29 03:19:58 +00:00
fn is_list ( & self ) -> bool {
2018-12-18 14:32:05 +00:00
self . type_ = = FieldType ::ListSingle | | self . type_ = = FieldType ::ListMulti
2017-07-29 03:19:58 +00:00
}
2024-05-09 08:35:43 +00:00
/// Return true if this field is a valid form type specifier as per
/// [XEP-0068](https://xmpp.org/extensions/xep-0068.html).
///
/// This function requires knowledge of the form's type attribute as the
2024-05-13 20:54:26 +00:00
/// criteria differ slightly among form types.
2024-05-09 08:35:43 +00:00
pub fn is_form_type ( & self , ty : & DataFormType ) -> bool {
// 1. A field must have the var FORM_TYPE
2023-08-30 13:55:18 +00:00
if self . var . as_deref ( ) ! = Some ( " FORM_TYPE " ) {
2024-05-09 08:35:43 +00:00
return false ;
}
match ty {
// https://xmpp.org/extensions/xep-0068.html#usecases-incorrect
// > If the FORM_TYPE field is not hidden in a form with
// > type="form" or type="result", it MUST be ignored as a context
// > indicator.
DataFormType ::Form | DataFormType ::Result_ = > self . type_ = = FieldType ::Hidden ,
// https://xmpp.org/extensions/xep-0068.html#impl
// > Data forms with the type "submit" are free to omit any
// > explicit field type declaration (as per Data Forms (XEP-0004)
// > § 3.2), as the type is implied by the corresponding
// > "form"-type data form. As consequence, implementations MUST
// > treat a FORM_TYPE field without an explicit type attribute,
// > in data forms of type "submit", as the FORM_TYPE field with
// > the special meaning defined herein.
2024-07-03 09:06:33 +00:00
DataFormType ::Submit = > matches! ( self . type_ , FieldType ::Hidden | FieldType ::TextSingle ) ,
2024-05-09 08:35:43 +00:00
// XEP-0068 does not explicitly mention cancel type forms.
// However, XEP-0004 states:
// > a data form of type "cancel" SHOULD NOT contain any <field/>
// > elements.
// thus we ignore those.
DataFormType ::Cancel = > false ,
}
}
2017-07-29 03:19:58 +00:00
}
impl TryFrom < Element > for Field {
2024-06-21 14:27:43 +00:00
type Error = FromElementError ;
2017-07-29 03:19:58 +00:00
2024-06-21 14:27:43 +00:00
fn try_from ( elem : Element ) -> Result < Field , FromElementError > {
2018-05-14 14:30:28 +00:00
check_self! ( elem , " field " , DATA_FORMS ) ;
2017-10-10 17:26:05 +00:00
check_no_unknown_attributes! ( elem , " field " , [ " label " , " type " , " var " ] ) ;
2017-07-29 03:19:58 +00:00
let mut field = Field {
2023-08-30 13:55:18 +00:00
var : get_attr ! ( elem , " var " , Option ) ,
2019-02-24 19:48:19 +00:00
type_ : get_attr ! ( elem , " type " , Default ) ,
label : get_attr ! ( elem , " label " , Option ) ,
2017-07-29 03:19:58 +00:00
required : false ,
2023-08-30 15:01:17 +00:00
desc : None ,
2018-12-18 14:32:05 +00:00
options : vec ! [ ] ,
values : vec ! [ ] ,
media : vec ! [ ] ,
2024-06-19 17:15:34 +00:00
validate : None ,
2017-07-29 03:19:58 +00:00
} ;
2023-08-30 13:55:18 +00:00
if field . type_ ! = FieldType ::Fixed & & field . var . is_none ( ) {
2024-06-21 14:27:43 +00:00
return Err ( Error ::Other ( " Required attribute 'var' missing. " ) . into ( ) ) ;
2023-08-30 13:55:18 +00:00
}
2017-07-29 03:19:58 +00:00
for element in elem . children ( ) {
if element . is ( " value " , ns ::DATA_FORMS ) {
2017-10-10 17:26:05 +00:00
check_no_children! ( element , " value " ) ;
2017-10-31 15:47:38 +00:00
check_no_attributes! ( element , " value " ) ;
2017-07-29 03:19:58 +00:00
field . values . push ( element . text ( ) ) ;
} else if element . is ( " required " , ns ::DATA_FORMS ) {
if field . required {
2024-06-21 14:27:43 +00:00
return Err ( Error ::Other ( " More than one required element. " ) . into ( ) ) ;
2017-07-29 03:19:58 +00:00
}
2017-10-10 17:26:05 +00:00
check_no_children! ( element , " required " ) ;
2017-10-31 15:47:38 +00:00
check_no_attributes! ( element , " required " ) ;
2017-07-29 03:19:58 +00:00
field . required = true ;
} else if element . is ( " option " , ns ::DATA_FORMS ) {
if ! field . is_list ( ) {
2024-06-21 14:27:43 +00:00
return Err ( Error ::Other ( " Option element found in non-list field. " ) . into ( ) ) ;
2017-07-29 03:19:58 +00:00
}
2017-10-10 17:26:05 +00:00
let option = Option_ ::try_from ( element . clone ( ) ) ? ;
field . options . push ( option ) ;
2017-07-29 03:19:58 +00:00
} else if element . is ( " media " , ns ::MEDIA_ELEMENT ) {
let media_element = MediaElement ::try_from ( element . clone ( ) ) ? ;
field . media . push ( media_element ) ;
2023-08-30 15:01:17 +00:00
} else if element . is ( " desc " , ns ::DATA_FORMS ) {
2024-06-18 15:22:23 +00:00
check_no_children! ( element , " desc " ) ;
check_no_attributes! ( element , " desc " ) ;
2023-08-30 15:01:17 +00:00
field . desc = Some ( element . text ( ) ) ;
2024-06-19 17:15:34 +00:00
} else if element . is ( " validate " , ns ::XDATA_VALIDATE ) {
if field . validate . is_some ( ) {
2024-06-24 13:24:19 +00:00
return Err ( Error ::Other ( " More than one validate element in field. " ) . into ( ) ) ;
2024-06-19 17:15:34 +00:00
}
field . validate = Some ( Validate ::try_from ( element . clone ( ) ) ? ) ;
2017-07-29 03:19:58 +00:00
} else {
2024-06-21 14:27:43 +00:00
return Err (
Error ::Other ( " Field child isn’ t a value, option or media element. " ) . into ( ) ,
) ;
2017-07-29 03:19:58 +00:00
}
}
Ok ( field )
}
}
2017-05-25 00:00:17 +00:00
impl From < Field > for Element {
fn from ( field : Field ) -> Element {
2020-03-28 12:07:26 +00:00
Element ::builder ( " field " , ns ::DATA_FORMS )
2018-12-18 14:32:05 +00:00
. attr ( " var " , field . var )
. attr ( " type " , field . type_ )
. attr ( " label " , field . label )
2019-09-06 14:03:58 +00:00
. append_all ( if field . required {
2020-03-28 12:07:26 +00:00
Some ( Element ::builder ( " required " , ns ::DATA_FORMS ) )
2018-12-18 14:32:05 +00:00
} else {
None
2019-09-06 14:03:58 +00:00
} )
. append_all ( field . options . iter ( ) . cloned ( ) . map ( Element ::from ) )
2019-07-24 22:20:38 +00:00
. append_all (
2018-12-18 14:32:05 +00:00
field
. values
. into_iter ( )
2020-03-28 12:07:26 +00:00
. map ( | value | Element ::builder ( " value " , ns ::DATA_FORMS ) . append ( value ) ) ,
2018-12-18 14:32:05 +00:00
)
2019-09-06 14:03:58 +00:00
. append_all ( field . media . iter ( ) . cloned ( ) . map ( Element ::from ) )
2024-06-19 17:15:34 +00:00
. append_all ( field . validate )
2018-12-18 14:32:05 +00:00
. build ( )
2017-05-25 00:00:17 +00:00
}
}
2018-08-08 17:35:32 +00:00
generate_attribute! (
/// Represents the type of a [data form](struct.DataForm.html).
DataFormType , " type " , {
/// This is a cancel request for a prior type="form" data form.
Cancel = > " cancel " ,
2017-05-25 00:00:17 +00:00
2018-08-08 17:35:32 +00:00
/// This is a request for the recipient to fill this form and send it
/// back as type="submit".
Form = > " form " ,
/// This is a result form, which contains what the requester asked for.
Result_ = > " result " ,
/// This is a complete response to a form received before.
Submit = > " submit " ,
}
) ;
/// This is a form to be sent to another entity for filling.
2020-11-29 20:17:51 +00:00
#[ derive(Debug, Clone, PartialEq) ]
2017-04-18 19:44:36 +00:00
pub struct DataForm {
2018-08-08 17:35:32 +00:00
/// The type of this form, telling the other party which action to execute.
2017-04-18 19:44:36 +00:00
pub type_ : DataFormType ,
2018-08-08 17:35:32 +00:00
/// An easy accessor for the FORM_TYPE of this form, see
/// [XEP-0068](https://xmpp.org/extensions/xep-0068.html) for more
/// information.
2017-04-18 19:44:36 +00:00
pub form_type : Option < String > ,
2018-08-08 17:35:32 +00:00
/// The title of this form.
2017-05-21 15:41:29 +00:00
pub title : Option < String > ,
2018-08-08 17:35:32 +00:00
/// The instructions given with this form.
2017-05-21 15:41:29 +00:00
pub instructions : Option < String > ,
2018-08-08 17:35:32 +00:00
/// A list of fields comprising this form.
2017-04-18 19:44:36 +00:00
pub fields : Vec < Field > ,
}
2022-01-03 10:50:58 +00:00
impl DataForm {
/// Create a new DataForm.
pub fn new ( type_ : DataFormType , form_type : & str , fields : Vec < Field > ) -> DataForm {
DataForm {
type_ ,
form_type : Some ( String ::from ( form_type ) ) ,
title : None ,
instructions : None ,
fields ,
}
}
}
2017-05-23 22:31:33 +00:00
impl TryFrom < Element > for DataForm {
2024-06-21 14:27:43 +00:00
type Error = FromElementError ;
2017-05-06 19:51:39 +00:00
2024-06-21 14:27:43 +00:00
fn try_from ( elem : Element ) -> Result < DataForm , FromElementError > {
2018-05-14 14:30:28 +00:00
check_self! ( elem , " x " , DATA_FORMS ) ;
2017-10-10 17:26:05 +00:00
check_no_unknown_attributes! ( elem , " x " , [ " type " ] ) ;
2019-02-24 19:48:19 +00:00
let type_ = get_attr! ( elem , " type " , Required ) ;
2017-05-21 15:41:29 +00:00
let mut form = DataForm {
2019-02-21 20:00:58 +00:00
type_ ,
2017-05-21 15:41:29 +00:00
form_type : None ,
title : None ,
instructions : None ,
2018-12-18 14:32:05 +00:00
fields : vec ! [ ] ,
2017-05-06 19:51:39 +00:00
} ;
2017-05-21 15:41:29 +00:00
for child in elem . children ( ) {
if child . is ( " title " , ns ::DATA_FORMS ) {
if form . title . is_some ( ) {
2024-06-21 14:27:43 +00:00
return Err ( Error ::Other ( " More than one title in form element. " ) . into ( ) ) ;
2017-05-21 15:41:29 +00:00
}
2017-10-10 17:26:05 +00:00
check_no_children! ( child , " title " ) ;
2017-10-31 15:47:38 +00:00
check_no_attributes! ( child , " title " ) ;
2017-05-21 15:41:29 +00:00
form . title = Some ( child . text ( ) ) ;
} else if child . is ( " instructions " , ns ::DATA_FORMS ) {
if form . instructions . is_some ( ) {
2024-06-21 14:27:43 +00:00
return Err ( Error ::Other ( " More than one instructions in form element. " ) . into ( ) ) ;
2017-05-21 15:41:29 +00:00
}
2017-10-10 17:26:05 +00:00
check_no_children! ( child , " instructions " ) ;
2017-10-31 15:47:38 +00:00
check_no_attributes! ( child , " instructions " ) ;
2017-05-21 15:41:29 +00:00
form . instructions = Some ( child . text ( ) ) ;
} else if child . is ( " field " , ns ::DATA_FORMS ) {
2017-07-29 03:19:58 +00:00
let field = Field ::try_from ( child . clone ( ) ) ? ;
2024-05-09 08:35:43 +00:00
if field . is_form_type ( & form . type_ ) {
2019-02-24 18:25:14 +00:00
let mut field = field ;
2017-05-21 15:41:29 +00:00
if form . form_type . is_some ( ) {
2024-06-21 14:27:43 +00:00
return Err ( Error ::Other ( " More than one FORM_TYPE in a data form. " ) . into ( ) ) ;
2017-05-06 19:51:39 +00:00
}
2017-05-21 15:41:29 +00:00
if field . values . len ( ) ! = 1 {
2024-06-21 14:27:43 +00:00
return Err ( Error ::Other ( " Wrong number of values in FORM_TYPE. " ) . into ( ) ) ;
2017-05-06 19:51:39 +00:00
}
2019-02-24 18:25:14 +00:00
form . form_type = field . values . pop ( ) ;
} else {
form . fields . push ( field ) ;
2017-04-18 19:44:36 +00:00
}
2017-05-06 19:51:39 +00:00
} else {
2024-06-21 14:27:43 +00:00
return Err ( Error ::Other ( " Unknown child in data form element. " ) . into ( ) ) ;
2017-04-18 19:44:36 +00:00
}
}
2017-05-21 15:41:29 +00:00
Ok ( form )
2017-04-18 19:44:36 +00:00
}
}
2017-05-25 00:00:17 +00:00
impl From < DataForm > for Element {
fn from ( form : DataForm ) -> Element {
2020-03-28 12:07:26 +00:00
Element ::builder ( " x " , ns ::DATA_FORMS )
2018-12-18 14:32:05 +00:00
. attr ( " type " , form . type_ )
2019-07-24 22:20:38 +00:00
. append_all (
2018-12-18 14:32:05 +00:00
form . title
2020-03-28 12:07:26 +00:00
. map ( | title | Element ::builder ( " title " , ns ::DATA_FORMS ) . append ( title ) ) ,
2018-12-18 14:32:05 +00:00
)
2020-04-02 20:45:20 +00:00
. append_all (
form . instructions
. map ( | text | Element ::builder ( " instructions " , ns ::DATA_FORMS ) . append ( text ) ) ,
)
2019-09-06 14:03:58 +00:00
. append_all ( form . form_type . map ( | form_type | {
2020-03-28 12:07:26 +00:00
Element ::builder ( " field " , ns ::DATA_FORMS )
2019-09-06 14:03:58 +00:00
. attr ( " var " , " FORM_TYPE " )
. attr ( " type " , " hidden " )
2020-04-02 20:45:20 +00:00
. append ( Element ::builder ( " value " , ns ::DATA_FORMS ) . append ( form_type ) )
2019-09-06 14:03:58 +00:00
} ) )
. append_all ( form . fields . iter ( ) . cloned ( ) . map ( Element ::from ) )
2018-12-18 14:32:05 +00:00
. build ( )
2017-05-25 00:00:17 +00:00
}
}
2017-04-18 19:44:36 +00:00
#[ cfg(test) ]
mod tests {
2017-05-06 19:51:39 +00:00
use super ::* ;
2024-06-30 09:25:45 +00:00
use crate ::data_forms_validate ::{ Datatype , Validate } ;
2017-04-18 19:44:36 +00:00
2018-10-28 12:10:48 +00:00
#[ cfg(target_pointer_width = " 32 " ) ]
#[ test ]
fn test_size ( ) {
assert_size! ( Option_ , 24 ) ;
assert_size! ( FieldType , 1 ) ;
2024-07-01 17:38:20 +00:00
assert_size! ( Field , 140 ) ;
2018-10-28 12:10:48 +00:00
assert_size! ( DataFormType , 1 ) ;
assert_size! ( DataForm , 52 ) ;
}
#[ cfg(target_pointer_width = " 64 " ) ]
2018-10-26 12:26:16 +00:00
#[ test ]
fn test_size ( ) {
assert_size! ( Option_ , 48 ) ;
assert_size! ( FieldType , 1 ) ;
2024-06-19 17:15:34 +00:00
assert_size! ( Field , 264 ) ;
2018-10-26 12:26:16 +00:00
assert_size! ( DataFormType , 1 ) ;
assert_size! ( DataForm , 104 ) ;
}
2017-04-18 19:44:36 +00:00
#[ test ]
fn test_simple ( ) {
let elem : Element = " <x xmlns='jabber:x:data' type='result'/> " . parse ( ) . unwrap ( ) ;
2017-05-23 22:31:33 +00:00
let form = DataForm ::try_from ( elem ) . unwrap ( ) ;
2017-05-06 19:51:39 +00:00
assert_eq! ( form . type_ , DataFormType ::Result_ ) ;
2017-04-18 19:44:36 +00:00
assert! ( form . form_type . is_none ( ) ) ;
assert! ( form . fields . is_empty ( ) ) ;
}
2023-08-30 13:55:18 +00:00
#[ test ]
fn test_missing_var ( ) {
let elem : Element =
" <x xmlns='jabber:x:data' type='form'><field type='text-single' label='The name of your bot'/></x> "
. parse ( )
. unwrap ( ) ;
let error = DataForm ::try_from ( elem ) . unwrap_err ( ) ;
let message = match error {
2024-06-21 14:27:43 +00:00
FromElementError ::Invalid ( Error ::Other ( string ) ) = > string ,
2023-08-30 13:55:18 +00:00
_ = > panic! ( ) ,
} ;
assert_eq! ( message , " Required attribute 'var' missing. " ) ;
}
#[ test ]
fn test_fixed_field ( ) {
let elem : Element =
" <x xmlns='jabber:x:data' type='form'><field type='fixed'><value>Section 1: Bot Info</value></field></x> "
. parse ( )
. unwrap ( ) ;
let form = DataForm ::try_from ( elem ) . unwrap ( ) ;
assert_eq! ( form . type_ , DataFormType ::Form ) ;
assert! ( form . form_type . is_none ( ) ) ;
assert_eq! (
form . fields ,
vec! [ Field {
var : None ,
type_ : FieldType ::Fixed ,
label : None ,
required : false ,
2023-08-30 15:01:17 +00:00
desc : None ,
2023-08-30 13:55:18 +00:00
options : vec ! [ ] ,
values : vec ! [ " Section 1: Bot Info " . to_string ( ) ] ,
media : vec ! [ ] ,
2024-06-19 17:15:34 +00:00
validate : None ,
2023-08-30 13:55:18 +00:00
} ]
) ;
}
2023-08-30 15:01:17 +00:00
#[ test ]
fn test_desc ( ) {
let elem : Element =
" <x xmlns='jabber:x:data' type='form'><field type='jid-multi' label='People to invite' var='invitelist'><desc>Tell all your friends about your new bot!</desc></field></x> "
. parse ( )
. unwrap ( ) ;
let form = DataForm ::try_from ( elem ) . unwrap ( ) ;
assert_eq! ( form . type_ , DataFormType ::Form ) ;
assert! ( form . form_type . is_none ( ) ) ;
assert_eq! (
form . fields ,
vec! [ Field {
var : Some ( " invitelist " . to_string ( ) ) ,
type_ : FieldType ::JidMulti ,
label : Some ( " People to invite " . to_string ( ) ) ,
required : false ,
desc : Some ( " Tell all your friends about your new bot! " . to_string ( ) ) ,
options : vec ! [ ] ,
values : vec ! [ ] ,
media : vec ! [ ] ,
2024-06-19 17:15:34 +00:00
validate : None ,
} ]
) ;
}
#[ test ]
fn test_validate ( ) {
let elem : Element = r #" <x xmlns='jabber:x:data' type='form'>
< field var = ' evt . date ' type = ' text - single ' label = ' Event Date / Time ' >
< validate xmlns = ' http ://jabber.org/protocol/xdata-validate' datatype='xs:dateTime'/>
< value > 2003 - 10 - 06 T11 :22 :00 - 07 :00 < / value >
< / field >
< / x > " #
. parse ( )
. unwrap ( ) ;
let form = DataForm ::try_from ( elem ) . unwrap ( ) ;
assert_eq! ( form . type_ , DataFormType ::Form ) ;
assert! ( form . form_type . is_none ( ) ) ;
assert_eq! (
form . fields ,
vec! [ Field {
var : Some ( " evt.date " . to_string ( ) ) ,
type_ : FieldType ::TextSingle ,
label : Some ( " Event Date/Time " . to_string ( ) ) ,
required : false ,
desc : None ,
options : vec ! [ ] ,
values : vec ! [ " 2003-10-06T11:22:00-07:00 " . to_string ( ) ] ,
media : vec ! [ ] ,
validate : Some ( Validate {
datatype : Some ( Datatype ::DateTime ) ,
method : None ,
list_range : None ,
} ) ,
2023-08-30 15:01:17 +00:00
} ]
) ;
}
2017-04-18 19:44:36 +00:00
#[ test ]
fn test_invalid ( ) {
let elem : Element = " <x xmlns='jabber:x:data'/> " . parse ( ) . unwrap ( ) ;
2017-05-23 22:31:33 +00:00
let error = DataForm ::try_from ( elem ) . unwrap_err ( ) ;
2017-04-18 19:44:36 +00:00
let message = match error {
2024-06-21 14:27:43 +00:00
FromElementError ::Invalid ( Error ::Other ( string ) ) = > string ,
2017-04-18 19:44:36 +00:00
_ = > panic! ( ) ,
} ;
2017-05-21 15:41:29 +00:00
assert_eq! ( message , " Required attribute 'type' missing. " ) ;
2017-04-18 19:44:36 +00:00
let elem : Element = " <x xmlns='jabber:x:data' type='coucou'/> " . parse ( ) . unwrap ( ) ;
2017-05-23 22:31:33 +00:00
let error = DataForm ::try_from ( elem ) . unwrap_err ( ) ;
2017-04-18 19:44:36 +00:00
let message = match error {
2024-06-21 14:27:43 +00:00
FromElementError ::Invalid ( Error ::TextParseError ( string ) ) = > string ,
other = > panic! ( " unexpected result: {:?} " , other ) ,
2017-04-18 19:44:36 +00:00
} ;
2024-06-21 14:27:43 +00:00
assert_eq! ( message . to_string ( ) , " Unknown value for 'type' attribute. " ) ;
2017-04-18 19:44:36 +00:00
}
#[ test ]
fn test_wrong_child ( ) {
2018-12-18 14:32:05 +00:00
let elem : Element = " <x xmlns='jabber:x:data' type='cancel'><coucou/></x> "
. parse ( )
. unwrap ( ) ;
2017-05-23 22:31:33 +00:00
let error = DataForm ::try_from ( elem ) . unwrap_err ( ) ;
2017-04-18 19:44:36 +00:00
let message = match error {
2024-06-21 14:27:43 +00:00
FromElementError ::Invalid ( Error ::Other ( string ) ) = > string ,
2017-04-18 19:44:36 +00:00
_ = > panic! ( ) ,
} ;
2017-05-21 15:41:29 +00:00
assert_eq! ( message , " Unknown child in data form element. " ) ;
2017-04-18 19:44:36 +00:00
}
2018-05-28 15:05:04 +00:00
#[ test ]
fn option ( ) {
2018-12-18 14:32:05 +00:00
let elem : Element =
" <option xmlns='jabber:x:data' label='Coucou !'><value>coucou</value></option> "
. parse ( )
. unwrap ( ) ;
2018-05-28 15:05:04 +00:00
let option = Option_ ::try_from ( elem ) . unwrap ( ) ;
assert_eq! ( & option . label . unwrap ( ) , " Coucou ! " ) ;
assert_eq! ( & option . value , " coucou " ) ;
2018-12-18 14:32:05 +00:00
let elem : Element = " <option xmlns='jabber:x:data' label='Coucou !'/> "
. parse ( )
. unwrap ( ) ;
2018-05-28 15:05:04 +00:00
let error = Option_ ::try_from ( elem ) . unwrap_err ( ) ;
let message = match error {
2024-06-21 14:27:43 +00:00
FromElementError ::Invalid ( Error ::Other ( string ) ) = > string ,
2018-05-28 15:05:04 +00:00
_ = > panic! ( ) ,
} ;
assert_eq! ( message , " Missing child value in option element. " ) ;
let elem : Element = " <option xmlns='jabber:x:data' label='Coucou !'><value>coucou</value><value>error</value></option> " . parse ( ) . unwrap ( ) ;
let error = Option_ ::try_from ( elem ) . unwrap_err ( ) ;
let message = match error {
2024-06-21 14:27:43 +00:00
FromElementError ::Invalid ( Error ::Other ( string ) ) = > string ,
2018-05-28 15:05:04 +00:00
_ = > panic! ( ) ,
} ;
2018-12-18 14:32:05 +00:00
assert_eq! (
message ,
" Element option must not have more than one value child. "
) ;
2018-05-28 15:05:04 +00:00
}
2024-05-09 08:35:43 +00:00
#[ test ]
fn test_ignore_form_type_field_if_field_type_mismatches_in_form_typed_forms ( ) {
// https://xmpp.org/extensions/xep-0068.html#usecases-incorrect
// […] it MUST be ignored as a context indicator
let elem : Element = " <x xmlns='jabber:x:data' type='form'><field var='FORM_TYPE' type='text-single'><value>foo</value></field></x> " . parse ( ) . unwrap ( ) ;
match DataForm ::try_from ( elem ) {
Ok ( form ) = > {
match form . form_type {
None = > ( ) ,
other = > panic! ( " unexpected extracted form type: {:?} " , other ) ,
} ;
}
other = > panic! ( " unexpected result: {:?} " , other ) ,
}
}
#[ test ]
fn test_ignore_form_type_field_if_field_type_mismatches_in_result_typed_forms ( ) {
// https://xmpp.org/extensions/xep-0068.html#usecases-incorrect
// […] it MUST be ignored as a context indicator
let elem : Element = " <x xmlns='jabber:x:data' type='result'><field var='FORM_TYPE' type='text-single'><value>foo</value></field></x> " . parse ( ) . unwrap ( ) ;
match DataForm ::try_from ( elem ) {
Ok ( form ) = > {
match form . form_type {
None = > ( ) ,
other = > panic! ( " unexpected extracted form type: {:?} " , other ) ,
} ;
}
other = > panic! ( " unexpected result: {:?} " , other ) ,
}
}
#[ test ]
fn test_accept_form_type_field_without_type_attribute_in_submit_typed_forms ( ) {
let elem : Element = " <x xmlns='jabber:x:data' type='submit'><field var='FORM_TYPE'><value>foo</value></field></x> " . parse ( ) . unwrap ( ) ;
match DataForm ::try_from ( elem ) {
Ok ( form ) = > {
match form . form_type {
Some ( ty ) = > assert_eq! ( ty , " foo " ) ,
other = > panic! ( " unexpected extracted form type: {:?} " , other ) ,
} ;
}
other = > panic! ( " unexpected result: {:?} " , other ) ,
}
}
#[ test ]
fn test_accept_form_type_field_with_type_hidden_in_submit_typed_forms ( ) {
let elem : Element = " <x xmlns='jabber:x:data' type='submit'><field var='FORM_TYPE' type='hidden'><value>foo</value></field></x> " . parse ( ) . unwrap ( ) ;
match DataForm ::try_from ( elem ) {
Ok ( form ) = > {
match form . form_type {
Some ( ty ) = > assert_eq! ( ty , " foo " ) ,
other = > panic! ( " unexpected extracted form type: {:?} " , other ) ,
} ;
}
other = > panic! ( " unexpected result: {:?} " , other ) ,
}
}
#[ test ]
fn test_accept_form_type_field_with_type_hidden_in_result_typed_forms ( ) {
let elem : Element = " <x xmlns='jabber:x:data' type='result'><field var='FORM_TYPE' type='hidden'><value>foo</value></field></x> " . parse ( ) . unwrap ( ) ;
match DataForm ::try_from ( elem ) {
Ok ( form ) = > {
match form . form_type {
Some ( ty ) = > assert_eq! ( ty , " foo " ) ,
other = > panic! ( " unexpected extracted form type: {:?} " , other ) ,
} ;
}
other = > panic! ( " unexpected result: {:?} " , other ) ,
}
}
#[ test ]
fn test_accept_form_type_field_with_type_hidden_in_form_typed_forms ( ) {
let elem : Element = " <x xmlns='jabber:x:data' type='form'><field var='FORM_TYPE' type='hidden'><value>foo</value></field></x> " . parse ( ) . unwrap ( ) ;
match DataForm ::try_from ( elem ) {
Ok ( form ) = > {
match form . form_type {
Some ( ty ) = > assert_eq! ( ty , " foo " ) ,
other = > panic! ( " unexpected extracted form type: {:?} " , other ) ,
} ;
}
other = > panic! ( " unexpected result: {:?} " , other ) ,
}
}
2017-04-18 19:44:36 +00:00
}