diff --git a/qbittorrent-web-api-gen/groups.txt b/qbittorrent-web-api-gen/groups.txt index 88f9685..c32ae2f 100644 --- a/qbittorrent-web-api-gen/groups.txt +++ b/qbittorrent-web-api-gen/groups.txt @@ -5835,18 +5835,15 @@ ReturnTypeParameter { name: "results", description: "Array of result objects- see table below", - return_type: ObjectArray( - TypeWithRef { - type_info: TypeInfo { - name: "results", - is_optional: false, - is_list: false, - description: Some( - "Array of result objects- see table below", - ), - type_description: None, - }, - ref_type: "result", + return_type: StringArray( + TypeInfo { + name: "results", + is_optional: false, + is_list: false, + description: Some( + "Array of result objects- see table below", + ), + type_description: None, }, ), }, diff --git a/qbittorrent-web-api-gen/src/parser/group/method/method_tests/search_result.check b/qbittorrent-web-api-gen/src/parser/group/method/method_tests/search_result.check new file mode 100644 index 0000000..5609adf --- /dev/null +++ b/qbittorrent-web-api-gen/src/parser/group/method/method_tests/search_result.check @@ -0,0 +1,100 @@ +ApiMethod { + name: "results", + description: Some( + "The response is a JSON object with the following fields\n\n\n\n\nExample:\n\n```JSON\n{\n \"results\": [\n {\n \"descrLink\": \"http://www.legittorrents.info/index.php?page=torrent-details&id=8d5f512e1acb687029b8d7cc6c5a84dce51d7a41\",\n \"fileName\": \"Ubuntu-10.04-32bit-NeTV.ova\",\n \"fileSize\": -1,\n \"fileUrl\": \"http://www.legittorrents.info/download.php?id=8d5f512e1acb687029b8d7cc6c5a84dce51d7a41&f=Ubuntu-10.04-32bit-NeTV.ova.torrent\",\n \"nbLeechers\": 1,\n \"nbSeeders\": 0,\n \"siteUrl\": \"http://www.legittorrents.info\"\n },\n {\n \"descrLink\": \"http://www.legittorrents.info/index.php?page=torrent-details&id=d5179f53e105dc2c2401bcfaa0c2c4936a6aa475\",\n \"fileName\": \"mangOH-Legato-17_06-Ubuntu-16_04.ova\",\n \"fileSize\": -1,\n \"fileUrl\": \"http://www.legittorrents.info/download.php?id=d5179f53e105dc2c2401bcfaa0c2c4936a6aa475&f=mangOH-Legato-17_06-Ubuntu-16_04.ova.torrent\",\n \"nbLeechers\": 0,\n \"nbSeeders\": 59,\n \"siteUrl\": \"http://www.legittorrents.info\"\n }\n ],\n \"status\": \"Running\",\n \"total\": 2\n}\n```", + ), + parameters: Some( + ApiParameters { + mandatory: [ + Number( + TypeInfo { + name: "id", + is_optional: false, + is_list: false, + description: Some( + "ID of the search job", + ), + type_description: None, + }, + ), + ], + optional: [ + Number( + TypeInfo { + name: "limit", + is_optional: true, + is_list: false, + description: Some( + "max number of results to return. 0 or negative means no limit", + ), + type_description: None, + }, + ), + Number( + TypeInfo { + name: "offset", + is_optional: true, + is_list: false, + description: Some( + "result to start at. A negative number means count backwards (e.g. -2 returns the 2 most recent results)", + ), + type_description: None, + }, + ), + ], + }, + ), + return_type: Some( + ReturnType { + is_list: false, + parameters: [ + ReturnTypeParameter { + name: "results", + description: "Array of result objects- see table below", + return_type: StringArray( + TypeInfo { + name: "results", + is_optional: false, + is_list: false, + description: Some( + "Array of result objects- see table below", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "status", + description: "Current status of the search job (either Running or Stopped)", + return_type: String( + TypeInfo { + name: "status", + is_optional: false, + is_list: false, + description: Some( + "Current status of the search job (either Running or Stopped)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "total", + description: "Total number of results. If the status is Running this number may continue to increase", + return_type: Number( + TypeInfo { + name: "total", + is_optional: false, + is_list: false, + description: Some( + "Total number of results. If the status is Running this number may continue to increase", + ), + type_description: None, + }, + ), + }, + ], + }, + ), + url: "results", +} \ No newline at end of file diff --git a/qbittorrent-web-api-gen/src/parser/group/method/method_tests/search_result.md b/qbittorrent-web-api-gen/src/parser/group/method/method_tests/search_result.md new file mode 100644 index 0000000..6ef3347 --- /dev/null +++ b/qbittorrent-web-api-gen/src/parser/group/method/method_tests/search_result.md @@ -0,0 +1,68 @@ +## Get search results ## + +Name: `results` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`id` | number | ID of the search job +`limit` _optional_ | number | max number of results to return. 0 or negative means no limit +`offset` _optional_ | number | result to start at. A negative number means count backwards (e.g. `-2` returns the 2 most recent results) + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Search job was not found +409 | Offset is too large, or too small (e.g. absolute value of negative number is greater than # results) +200 | All other scenarios- see JSON below + +The response is a JSON object with the following fields + +Field | Type | Description +----------------------------------|---------|------------ +`results` | array | Array of `result` objects- see table below +`status` | string | Current status of the search job (either `Running` or `Stopped`) +`total` | number | Total number of results. If the status is `Running` this number may continue to increase + +**Result object:** + +Field | Type | Description +----------------------------------|---------|------------ +`descrLink` | string | URL of the torrent's description page +`fileName` | string | Name of the file +`fileSize` | number | Size of the file in Bytes +`fileUrl` | string | Torrent download link (usually either .torrent file or magnet link) +`nbLeechers` | number | Number of leechers +`nbSeeders` | number | Number of seeders +`siteUrl` | string | URL of the torrent site + +Example: + +```JSON +{ + "results": [ + { + "descrLink": "http://www.legittorrents.info/index.php?page=torrent-details&id=8d5f512e1acb687029b8d7cc6c5a84dce51d7a41", + "fileName": "Ubuntu-10.04-32bit-NeTV.ova", + "fileSize": -1, + "fileUrl": "http://www.legittorrents.info/download.php?id=8d5f512e1acb687029b8d7cc6c5a84dce51d7a41&f=Ubuntu-10.04-32bit-NeTV.ova.torrent", + "nbLeechers": 1, + "nbSeeders": 0, + "siteUrl": "http://www.legittorrents.info" + }, + { + "descrLink": "http://www.legittorrents.info/index.php?page=torrent-details&id=d5179f53e105dc2c2401bcfaa0c2c4936a6aa475", + "fileName": "mangOH-Legato-17_06-Ubuntu-16_04.ova", + "fileSize": -1, + "fileUrl": "http://www.legittorrents.info/download.php?id=d5179f53e105dc2c2401bcfaa0c2c4936a6aa475&f=mangOH-Legato-17_06-Ubuntu-16_04.ova.torrent", + "nbLeechers": 0, + "nbSeeders": 59, + "siteUrl": "http://www.legittorrents.info" + } + ], + "status": "Running", + "total": 2 +} +``` \ No newline at end of file diff --git a/qbittorrent-web-api-gen/src/parser/group/method/method_tests/search_result.tree b/qbittorrent-web-api-gen/src/parser/group/method/method_tests/search_result.tree new file mode 100644 index 0000000..6e9c73c --- /dev/null +++ b/qbittorrent-web-api-gen/src/parser/group/method/method_tests/search_result.tree @@ -0,0 +1,327 @@ +TokenTree { + title: None, + content: [], + children: [ + TokenTree { + title: Some( + "Get search results", + ), + content: [ + Text( + "", + ), + Text( + "Name: `results`", + ), + Text( + "", + ), + Asterisk( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`id` | number | ID of the search job", + columns: [ + "id", + "number", + "ID of the search job", + ], + }, + TableRow { + raw: "`limit` _optional_ | number | max number of results to return. 0 or negative means no limit", + columns: [ + "limit _optional_", + "number", + "max number of results to return. 0 or negative means no limit", + ], + }, + TableRow { + raw: "`offset` _optional_ | number | result to start at. A negative number means count backwards (e.g. `-2` returns the 2 most recent results)", + columns: [ + "offset _optional_", + "number", + "result to start at. A negative number means count backwards (e.g. -2 returns the 2 most recent results)", + ], + }, + ], + }, + ), + Text( + "", + ), + Asterisk( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "404 | Search job was not found", + columns: [ + "404", + "Search job was not found", + ], + }, + TableRow { + raw: "409 | Offset is too large, or too small (e.g. absolute value of negative number is greater than # results)", + columns: [ + "409", + "Offset is too large, or too small (e.g. absolute value of negative number is greater than # results)", + ], + }, + TableRow { + raw: "200 | All other scenarios- see JSON below", + columns: [ + "200", + "All other scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "", + ), + Text( + "The response is a JSON object with the following fields", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Field | Type | Description", + columns: [ + "Field", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`results` | array | Array of `result` objects- see table below", + columns: [ + "results", + "array", + "Array of result objects- see table below", + ], + }, + TableRow { + raw: "`status` | string | Current status of the search job (either `Running` or `Stopped`)", + columns: [ + "status", + "string", + "Current status of the search job (either Running or Stopped)", + ], + }, + TableRow { + raw: "`total` | number | Total number of results. If the status is `Running` this number may continue to increase", + columns: [ + "total", + "number", + "Total number of results. If the status is Running this number may continue to increase", + ], + }, + ], + }, + ), + Text( + "", + ), + Asterisk( + "Result object:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Field | Type | Description", + columns: [ + "Field", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`descrLink` | string | URL of the torrent's description page", + columns: [ + "descrLink", + "string", + "URL of the torrent's description page", + ], + }, + TableRow { + raw: "`fileName` | string | Name of the file", + columns: [ + "fileName", + "string", + "Name of the file", + ], + }, + TableRow { + raw: "`fileSize` | number | Size of the file in Bytes", + columns: [ + "fileSize", + "number", + "Size of the file in Bytes", + ], + }, + TableRow { + raw: "`fileUrl` | string | Torrent download link (usually either .torrent file or magnet link)", + columns: [ + "fileUrl", + "string", + "Torrent download link (usually either .torrent file or magnet link)", + ], + }, + TableRow { + raw: "`nbLeechers` | number | Number of leechers", + columns: [ + "nbLeechers", + "number", + "Number of leechers", + ], + }, + TableRow { + raw: "`nbSeeders` | number | Number of seeders", + columns: [ + "nbSeeders", + "number", + "Number of seeders", + ], + }, + TableRow { + raw: "`siteUrl` | string | URL of the torrent site", + columns: [ + "siteUrl", + "string", + "URL of the torrent site", + ], + }, + ], + }, + ), + Text( + "", + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "{", + ), + Text( + " \"results\": [", + ), + Text( + " {", + ), + Text( + " \"descrLink\": \"http://www.legittorrents.info/index.php?page=torrent-details&id=8d5f512e1acb687029b8d7cc6c5a84dce51d7a41\",", + ), + Text( + " \"fileName\": \"Ubuntu-10.04-32bit-NeTV.ova\",", + ), + Text( + " \"fileSize\": -1,", + ), + Text( + " \"fileUrl\": \"http://www.legittorrents.info/download.php?id=8d5f512e1acb687029b8d7cc6c5a84dce51d7a41&f=Ubuntu-10.04-32bit-NeTV.ova.torrent\",", + ), + Text( + " \"nbLeechers\": 1,", + ), + Text( + " \"nbSeeders\": 0,", + ), + Text( + " \"siteUrl\": \"http://www.legittorrents.info\"", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"descrLink\": \"http://www.legittorrents.info/index.php?page=torrent-details&id=d5179f53e105dc2c2401bcfaa0c2c4936a6aa475\",", + ), + Text( + " \"fileName\": \"mangOH-Legato-17_06-Ubuntu-16_04.ova\",", + ), + Text( + " \"fileSize\": -1,", + ), + Text( + " \"fileUrl\": \"http://www.legittorrents.info/download.php?id=d5179f53e105dc2c2401bcfaa0c2c4936a6aa475&f=mangOH-Legato-17_06-Ubuntu-16_04.ova.torrent\",", + ), + Text( + " \"nbLeechers\": 0,", + ), + Text( + " \"nbSeeders\": 59,", + ), + Text( + " \"siteUrl\": \"http://www.legittorrents.info\"", + ), + Text( + " }", + ), + Text( + " ],", + ), + Text( + " \"status\": \"Running\",", + ), + Text( + " \"total\": 2", + ), + Text( + "}", + ), + Text( + "```", + ), + ], + children: [], + }, + ], +} \ No newline at end of file diff --git a/qbittorrent-web-api-gen/src/parser/group/method/mod.rs b/qbittorrent-web-api-gen/src/parser/group/method/mod.rs index a2a72b1..08d27b8 100644 --- a/qbittorrent-web-api-gen/src/parser/group/method/mod.rs +++ b/qbittorrent-web-api-gen/src/parser/group/method/mod.rs @@ -1,15 +1,15 @@ mod description; -mod parameters; mod return_type; mod url; +use std::collections::HashMap; + use crate::{md_parser, parser::util, types}; pub use return_type::ReturnType; use self::{ - description::parse_method_description, parameters::parse_parameters, - return_type::parse_return_type, url::get_method_url, + description::parse_method_description, return_type::parse_return_type, url::get_method_url, }; #[derive(Debug)] @@ -60,9 +60,10 @@ pub fn parse_api_method(child: &md_parser::TokenTree) -> Option { } fn to_api_method(child: &md_parser::TokenTree, name: &str) -> ApiMethod { + let tables = child.to_tables(); let method_description = parse_method_description(&child.content); let return_type = parse_return_type(&child.content); - let parameters = parse_parameters(&child.content).map(ApiParameters::new); + let parameters = tables.parameters().map(ApiParameters::new); let method_url = get_method_url(&child.content); ApiMethod { @@ -73,3 +74,152 @@ fn to_api_method(child: &md_parser::TokenTree, name: &str) -> ApiMethod { url: method_url, } } + +impl md_parser::TokenTree { + fn to_tables(&self) -> Tables<'_> { + let mut tables = HashMap::new(); + let mut prev_prev: Option<&md_parser::MdContent> = None; + let mut prev: Option<&md_parser::MdContent> = None; + + for content in &self.content { + if let md_parser::MdContent::Table(table) = content { + let title = match prev_prev { + Some(md_parser::MdContent::Text(text)) => text.clone(), + Some(md_parser::MdContent::Asterisk(text)) => text.clone(), + _ => panic!("Expected table title, found: {:?}", prev_prev), + }; + + tables.insert(title.replace(':', ""), table); + } + + prev_prev = prev; + prev = Some(content); + } + + Tables { tables } + } +} + +#[derive(Debug)] +struct Tables<'a> { + tables: HashMap, +} + +impl<'a> Tables<'a> { + fn parameters(&self) -> Option> { + self.get_type_containing("Parameters") + } + + // fn return_type(&self) -> Option> { + // self.get_type_containing("Returns") + // } + + fn get_type_containing(&self, name: &str) -> Option> { + self.tables + .iter() + .find(|(key, _)| key.contains(name)) + .map(|(_, table)| table.to_types()) + } +} + +impl md_parser::Table { + fn to_types(&self) -> Vec { + self.rows + .iter() + .flat_map(|table_row| table_row.to_type()) + .collect() + } +} + +impl md_parser::TableRow { + fn to_type(&self) -> Option { + let columns = &self.columns; + let type_map = HashMap::new(); + let description = columns.get(2).cloned(); + + match &columns.get(2) { + // If the description contains a default value it means that the parameter is optional. + Some(desc) if desc.contains("default: ") => { + // type defines a variable as default if it contains: _optional_ + let name_with_optional = format!("{} {}", columns[0], types::OPTIONAL); + types::Type::from(&columns[1], &name_with_optional, description, &type_map) + } + _ => types::Type::from(&columns[1], &columns[0], description, &type_map), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use md_parser::TokenTreeFactory; + + macro_rules! TEST_DIR { + () => { + "method_tests" + }; + } + + #[allow(unused_macros)] + macro_rules! run_test { + ($test_file:expr) => { + use pretty_assertions::assert_eq; + + // given + let input = include_str!(concat!(TEST_DIR!(), "/", $test_file, ".md")); + + // when + let tree = TokenTreeFactory::create(input); + let api_method = parse_api_method(&tree.children[0]).unwrap(); + + // then + let api_method_as_str = format!("{api_method:#?}"); + let should_be = include_str!(concat!(TEST_DIR!(), "/", $test_file, ".check")); + assert_eq!(api_method_as_str, should_be); + }; + } + + // use this macro when creating/updating as test + #[allow(unused_macros)] + macro_rules! update_test { + ($test_file:expr) => { + use std::fs; + use std::path::Path; + + let input = include_str!(concat!(TEST_DIR!(), "/", $test_file, ".md")); + let tree = TokenTreeFactory::create(input); + let api_method = parse_api_method(&tree.children[0]).unwrap(); + + let tree_as_str = format!("{tree:#?}"); + let api_method_as_str = format!("{api_method:#?}"); + + let tree_file = concat!( + "src/parser/group/method/", + TEST_DIR!(), + "/", + $test_file, + ".tree" + ); + let file = concat!( + "src/parser/group/method/", + TEST_DIR!(), + "/", + $test_file, + ".check" + ); + + // prevent user from accidentally leaving the current macro in a test + if Path::new(file).exists() { + panic!("Test case already exists: {file}"); + } + + fs::write(file, api_method_as_str).unwrap(); + fs::write(tree_file, tree_as_str).unwrap(); + }; + } + + #[test] + fn search_result() { + run_test!("search_result"); + } +} diff --git a/qbittorrent-web-api-gen/src/parser/group/method/parameters.rs b/qbittorrent-web-api-gen/src/parser/group/method/parameters.rs deleted file mode 100644 index 189c160..0000000 --- a/qbittorrent-web-api-gen/src/parser/group/method/parameters.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::collections::HashMap; - -use crate::{md_parser, parser::types}; - -pub fn parse_parameters(content: &[md_parser::MdContent]) -> Option> { - let mut it = content - .iter() - .skip_while(|row| match row { - md_parser::MdContent::Asterisk(content) | md_parser::MdContent::Text(content) => { - !content.starts_with("Parameters:") - } - _ => true, - }) - // Parameters: <-- skip - // <-- skip - // table with parameters <-- take - .skip(2); - - let parameter_table = match it.next() { - Some(md_parser::MdContent::Table(table)) => table, - _ => return None, - }; - - // empty for now - let type_map = HashMap::default(); - - let table = parameter_table - .rows - .iter() - .flat_map(|row| parse_parameter(row, &type_map)) - .collect(); - - Some(table) -} - -fn parse_parameter( - row: &md_parser::TableRow, - type_map: &HashMap, -) -> Option { - let description = row.columns.get(2).cloned(); - - match &row.columns.get(2) { - // If the description contains a default value it means that the parameter is optional. - Some(desc) if desc.contains("default: ") => { - // type defines a variable as default if it contains: _optional_ - let name_with_optional = format!("{} {}", row.columns[0], types::OPTIONAL); - types::Type::from(&row.columns[1], &name_with_optional, description, type_map) - } - _ => types::Type::from(&row.columns[1], &row.columns[0], description, type_map), - } -} diff --git a/qbittorrent-web-api-gen/src/types.rs b/qbittorrent-web-api-gen/src/types.rs index 5f49d9d..d56f58b 100644 --- a/qbittorrent-web-api-gen/src/types.rs +++ b/qbittorrent-web-api-gen/src/types.rs @@ -1,4 +1,3 @@ -use regex::RegexBuilder; use std::collections::HashMap; #[derive(Debug, Clone)] @@ -45,6 +44,12 @@ pub struct TypeWithRef { pub ref_type: String, } +#[derive(Debug, Clone)] +pub struct ComplexObject { + pub type_info: TypeInfo, + pub fields: Vec, +} + pub const OPTIONAL: &str = "_optional_"; #[derive(Debug, Clone)] @@ -54,8 +59,8 @@ pub enum Type { Bool(TypeInfo), String(TypeInfo), StringArray(TypeInfo), - ObjectArray(TypeWithRef), Object(TypeInfo), + // ComplexObject(ComplexObject), } impl Type { @@ -67,7 +72,7 @@ impl Type { Type::String(_) => "String".into(), Type::StringArray(_) => "String".into(), Type::Object(_) => "String".into(), - Type::ObjectArray(_) => panic!("Not implemented for ObjectArray"), + // Type::ComplexObject(_) => panic!("Not implemented for ComplexObject"), } } @@ -83,7 +88,7 @@ impl Type { Type::String(_) => "str".into(), Type::StringArray(_) => "&[str]".into(), Type::Object(_) => "str".into(), - Type::ObjectArray(_) => panic!("Not implemented for ObjectArray"), + // Type::ComplexObject(_) => panic!("Not implemented for ComplexObject"), } } @@ -99,7 +104,7 @@ impl Type { Type::String(t) => t, Type::StringArray(t) => t, Type::Object(t) => t, - Type::ObjectArray(TypeWithRef { type_info, .. }) => type_info, + // Type::ComplexObject(ComplexObject { type_info, .. }) => type_info, } } @@ -131,16 +136,17 @@ impl Type { "bool" => Some(Type::Bool(create_type_info())), "integer" | "number" | "int" => Some(Type::Number(create_type_info())), "string" => Some(Type::String(create_type_info())), - "array" => description - .clone() - .and_then(|ref desc| get_ref_type(desc)) - .map(|ref_type| { - Type::ObjectArray(TypeWithRef { - type_info: create_type_info(), - ref_type, - }) - }) - .or_else(|| Some(Type::StringArray(create_type_info()))), + "array" => Some(Type::StringArray(create_type_info())), + // "array" => description + // .clone() + // .and_then(|ref desc| get_ref_type(desc)) + // .map(|ref_type| { + // Type::ObjectArray(TypeWithRef { + // type_info: create_type_info(), + // ref_type, + // }) + // }) + // .or_else(|| Some(Type::StringArray(create_type_info()))), "object" => Some(Type::Object(create_type_info())), "float" => Some(Type::Float(create_type_info())), _ => None, @@ -148,24 +154,24 @@ impl Type { } } -fn get_ref_type(desc: &str) -> Option { - let re = RegexBuilder::new(r".*array of (\w+)\s?.*") - .case_insensitive(true) - .build() - .unwrap(); +// fn get_ref_type(desc: &str) -> Option { +// let re = RegexBuilder::new(r".*array of (\w+)\s?.*") +// .case_insensitive(true) +// .build() +// .unwrap(); - re.captures(desc) - .and_then(|captures| captures.get(1)) - .map(|m| m.as_str().to_owned()) -} +// re.captures(desc) +// .and_then(|captures| captures.get(1)) +// .map(|m| m.as_str().to_owned()) +// } #[cfg(test)] mod tests { - use super::*; + // use super::*; - #[test] - fn should_parse_object_array() { - let ref_type = get_ref_type("Array of result objects- see table below"); - assert_eq!("result", ref_type.unwrap()); - } + // #[test] + // fn should_parse_object_array() { + // let ref_type = get_ref_type("Array of result objects- see table below"); + // assert_eq!("result", ref_type.unwrap()); + // } }