@@ -17,6 +17,7 @@ const EMPTY_CTYPE: &str = "/*EMPTY*/";
1717struct ParsedType {
1818 tid : TypeId ,
1919 c_type : Option < String > ,
20+ is_array : bool ,
2021 array_length : Option < u32 > ,
2122 zero_terminated : Option < bool > ,
2223}
@@ -1267,7 +1268,7 @@ impl Library {
12671268 let transfer = elem
12681269 . attr_from_str ( "transfer-ownership" ) ?
12691270 . unwrap_or ( Transfer :: None ) ;
1270- let nullable = elem. attr_bool ( "nullable" , false ) ;
1271+ let mut nullable = elem. attr_bool ( "nullable" , false ) ;
12711272 let scope = elem. attr_from_str ( "scope" ) ?. unwrap_or ( ParameterScope :: None ) ;
12721273 let closure = elem. attr_from_str ( "closure" ) ?;
12731274 let destroy = elem. attr_from_str ( "destroy" ) ?;
@@ -1305,6 +1306,7 @@ impl Library {
13051306 }
13061307
13071308 typ = Some ( elem_type) ;
1309+
13081310 Ok ( ( ) )
13091311 }
13101312 "varargs" => {
@@ -1317,9 +1319,32 @@ impl Library {
13171319 } ) ?;
13181320
13191321 if let Some ( mut elem_type) = typ {
1320- if for_method {
1321- elem_type. array_length = elem_type. array_length . map ( |l| l + 1 ) ;
1322+ if elem_type. is_array {
1323+ // Note: these could be handled in the `analysis` part, however that would be quite
1324+ // annoying because many code places check the parsed `nullable` instead of
1325+ // the `analysis` result. Gir necessitates a rewrite...
1326+ use ParameterDirection :: * ;
1327+ match direction {
1328+ Return | Out | InOut => {
1329+ // Some arrays in return position come with nullable=true & zero-terminated=false
1330+ // annotations. In practice, the C implementations return NULL when the size is 0.
1331+ // So it doesn't make much sense using an `Option<_>` by default for nullable arrays.
1332+ nullable = false
1333+ }
1334+ _ => {
1335+ // We can only generate a Rust `Option` from a nullable array annotation
1336+ // if the array is not zero terminated. Otherwise we would need an extra
1337+ // indication to differentiate an empty array from an undefined value,
1338+ // which should be indicated in the doc and would need manual implementation.
1339+ nullable &= !elem_type. zero_terminated . unwrap_or ( true ) ;
1340+ }
1341+ }
1342+
1343+ if for_method {
1344+ elem_type. array_length = elem_type. array_length . map ( |l| l + 1 ) ;
1345+ }
13221346 }
1347+
13231348 Ok ( Parameter {
13241349 name : param_name. into ( ) ,
13251350 typ : elem_type. tid ,
@@ -1329,6 +1354,7 @@ impl Library {
13291354 transfer,
13301355 caller_allocates,
13311356 nullable : Nullable ( nullable) ,
1357+ is_array : elem_type. is_array ,
13321358 array_length : elem_type. array_length ,
13331359 zero_terminated : elem_type. zero_terminated ,
13341360 is_error : false ,
@@ -1451,6 +1477,7 @@ impl Library {
14511477 construct,
14521478 construct_only,
14531479 transfer,
1480+ is_array : elem_type. is_array ,
14541481 typ : elem_type. tid ,
14551482 c_type : elem_type. c_type ,
14561483 version,
@@ -1468,7 +1495,7 @@ impl Library {
14681495 ns_id : u16 ,
14691496 elem : & Element ,
14701497 ) -> Result < ParsedType , String > {
1471- let type_name = elem
1498+ let mut type_name = elem
14721499 . attr ( "name" )
14731500 . or_else ( || {
14741501 if elem. name ( ) == "array" {
@@ -1508,19 +1535,15 @@ impl Library {
15081535 "<type> element is missing an inner element type" ,
15091536 elem. position ( ) ,
15101537 ) )
1511- } else if type_name == "gboolean" && c_type. as_deref ( ) == Some ( "_Bool" ) {
1512- Ok ( ParsedType {
1513- tid : self . find_or_stub_type ( ns_id, "bool" ) ,
1514- c_type,
1515- array_length,
1516- zero_terminated,
1517- } )
15181538 } else {
1539+ if type_name == "gboolean" && c_type. as_deref ( ) == Some ( "_Bool" ) {
1540+ type_name = "bool" ;
1541+ }
1542+
15191543 Ok ( ParsedType {
15201544 tid : self . find_or_stub_type ( ns_id, type_name) ,
15211545 c_type,
1522- array_length,
1523- zero_terminated,
1546+ ..Default :: default ( )
15241547 } )
15251548 }
15261549 } else {
@@ -1541,6 +1564,7 @@ impl Library {
15411564 Ok ( ParsedType {
15421565 tid,
15431566 c_type,
1567+ is_array : true ,
15441568 array_length,
15451569 zero_terminated,
15461570 } )
@@ -1637,4 +1661,89 @@ mod tests {
16371661 } )
16381662 . unwrap ( ) ;
16391663 }
1664+
1665+ #[ test]
1666+ fn array_param_nullable_zero_terminated_default ( ) {
1667+ XmlParser :: new (
1668+ br#"<?xml version="1.0"?>
1669+ <parameter name="data" transfer-ownership="none" nullable="1">
1670+ <array length="2" type="const guchar*">
1671+ <type name="guint8" type="guchar"/>
1672+ </array>
1673+ </parameter>"#
1674+ . as_slice ( ) ,
1675+ )
1676+ . document ( |p, _| {
1677+ p. element_with_name ( "parameter" , |p, elem| {
1678+ let elem = Library :: new ( "Glib" )
1679+ . read_parameter ( p, MAIN_NAMESPACE , elem, false , false )
1680+ . unwrap ( ) ;
1681+ assert_eq ! ( elem. c_type, "const guchar*" ) ;
1682+ // We can only generate a Rust `Option` from a nullable array annotation
1683+ // if the array is not zero terminated. Otherwise we would need an extra
1684+ // indication to differentiate an empty array from an undefined value,
1685+ // which should be indicated in the doc and would need manual implementation.
1686+ assert ! ( !* elem. nullable) ;
1687+ // the default seems to be true
1688+ assert ! ( elem. zero_terminated. is_none( ) ) ;
1689+
1690+ Ok ( ( ) )
1691+ } )
1692+ } )
1693+ . unwrap ( ) ;
1694+ }
1695+
1696+ #[ test]
1697+ fn array_param_nullable_zero_terminated ( ) {
1698+ XmlParser :: new (
1699+ br#"<?xml version="1.0"?>
1700+ <parameter name="data" transfer-ownership="none" nullable="1">
1701+ <array length="2" zero-terminated="1" type="const guchar*">
1702+ <type name="guint8" type="guchar"/>
1703+ </array>
1704+ </parameter>"#
1705+ . as_slice ( ) ,
1706+ )
1707+ . document ( |p, _| {
1708+ p. element_with_name ( "parameter" , |p, elem| {
1709+ let elem = Library :: new ( "Glib" )
1710+ . read_parameter ( p, MAIN_NAMESPACE , elem, false , false )
1711+ . unwrap ( ) ;
1712+ assert_eq ! ( elem. c_type, "const guchar*" ) ;
1713+ // We can only generate a Rust `Option` from a nullable array annotation
1714+ // if the array is not zero terminated. Otherwise we would need an extra
1715+ // indication to differentiate an empty array from an undefined value,
1716+ // which should be indicated in the doc and would need manual implementation.
1717+ assert ! ( !* elem. nullable) ;
1718+ assert ! ( elem. zero_terminated. unwrap( ) ) ;
1719+
1720+ Ok ( ( ) )
1721+ } )
1722+ } )
1723+ . unwrap ( ) ;
1724+ }
1725+
1726+ #[ test]
1727+ fn array_prop ( ) {
1728+ XmlParser :: new (
1729+ br#"<?xml version="1.0"?>
1730+ <property name="advertised-protocols" version="2.60" writable="1" transfer-ownership="none" setter="set_advertised_protocols">
1731+ <array>
1732+ <type name="utf8"/>
1733+ </array>
1734+ </property>"#
1735+ . as_slice ( ) ,
1736+ )
1737+ . document ( |p, _| {
1738+ p. element_with_name ( "property" , |p, elem| {
1739+ let elem = Library :: new ( "Glib" )
1740+ . read_property ( p, MAIN_NAMESPACE , elem, "" )
1741+ . unwrap ( ) . unwrap ( ) ;
1742+ assert ! ( elem. is_array) ;
1743+
1744+ Ok ( ( ) )
1745+ } )
1746+ } )
1747+ . unwrap ( ) ;
1748+ }
16401749}
0 commit comments