feat: signal support
also ran formatter
This commit is contained in:
		
							parent
							
								
									0981ac7345
								
							
						
					
					
						commit
						3fc1aa19fd
					
				
					 6 changed files with 392 additions and 149 deletions
				
			
		| 
						 | 
				
			
			@ -98,6 +98,21 @@
 | 
			
		|||
</ul>
 | 
			
		||||
{{- end -}}
 | 
			
		||||
 | 
			
		||||
{{- if $type.signals -}}
 | 
			
		||||
<h4>Signals</h4>
 | 
			
		||||
<ul>
 | 
			
		||||
	{{- range $signame, $sig := $type.signals -}}
 | 
			
		||||
	<li>
 | 
			
		||||
		<span class="typegray">
 | 
			
		||||
			<a href="#signal.{{ $signame }}">{{ $signame }}</a>(
 | 
			
		||||
				{{- partial "qmlparams.html" $sig.params -}}
 | 
			
		||||
			)
 | 
			
		||||
		</span>
 | 
			
		||||
	</li>
 | 
			
		||||
	{{- end -}}
 | 
			
		||||
</ul>
 | 
			
		||||
{{- end -}}
 | 
			
		||||
 | 
			
		||||
{{- if $type.variants -}}
 | 
			
		||||
<h4>Variants</h4>
 | 
			
		||||
<ul>
 | 
			
		||||
| 
						 | 
				
			
			@ -186,6 +201,34 @@
 | 
			
		|||
	{{- end -}}
 | 
			
		||||
{{- end -}}
 | 
			
		||||
 | 
			
		||||
{{- if $type.signals -}}
 | 
			
		||||
	<h3>Signal Details</h3>
 | 
			
		||||
	{{ range $signame, $sig := $type.signals }}
 | 
			
		||||
		<div id="signal.{{ $signame }}" class = "qmlpropdef">
 | 
			
		||||
			{{- if $sig.flags -}}
 | 
			
		||||
				<span class="qmlprops typegray">
 | 
			
		||||
					{{ partial "qmltypeflags.html" $sig.flags }}
 | 
			
		||||
				</span>
 | 
			
		||||
			{{- end -}}
 | 
			
		||||
 | 
			
		||||
			<p>
 | 
			
		||||
				{{ $signame -}}
 | 
			
		||||
				<span class="typegray">(
 | 
			
		||||
					{{- partial "qmlparams.html" $sig.params -}}
 | 
			
		||||
				)</span>
 | 
			
		||||
			</p>
 | 
			
		||||
		</div>
 | 
			
		||||
 | 
			
		||||
		<div class="qmlpropdetails">
 | 
			
		||||
			{{- if $sig.details -}}
 | 
			
		||||
				{{- $sig.details | $.Page.RenderString (dict "display" "block") -}}
 | 
			
		||||
			{{- else -}}
 | 
			
		||||
				<p style="color: #999999"><i>No details provided.</i></p>
 | 
			
		||||
			{{- end -}}
 | 
			
		||||
		</div>
 | 
			
		||||
	{{- end -}}
 | 
			
		||||
{{- end -}}
 | 
			
		||||
 | 
			
		||||
{{- if $type.variants -}}
 | 
			
		||||
	<h3>Variant Details</h3>
 | 
			
		||||
	{{ range $name, $variant := $type.variants }}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,10 +2,10 @@ use std::{collections::HashMap, path::Path};
 | 
			
		|||
 | 
			
		||||
use anyhow::{anyhow, Context};
 | 
			
		||||
 | 
			
		||||
mod parse;
 | 
			
		||||
mod typespec;
 | 
			
		||||
mod outform;
 | 
			
		||||
mod parse;
 | 
			
		||||
mod resolver;
 | 
			
		||||
mod typespec;
 | 
			
		||||
 | 
			
		||||
fn main() -> anyhow::Result<()> {
 | 
			
		||||
	let args = std::env::args().collect::<Vec<_>>();
 | 
			
		||||
| 
						 | 
				
			
			@ -19,10 +19,17 @@ fn main() -> anyhow::Result<()> {
 | 
			
		|||
			let text = std::fs::read_to_string(path).expect("failed to read module file");
 | 
			
		||||
			let module = parse::parse_module(&text)?;
 | 
			
		||||
 | 
			
		||||
			let texts = module.header.headers.iter()
 | 
			
		||||
			let texts = module
 | 
			
		||||
				.header
 | 
			
		||||
				.headers
 | 
			
		||||
				.iter()
 | 
			
		||||
				.map(|header| {
 | 
			
		||||
					let text = std::fs::read_to_string(dir.join(header))
 | 
			
		||||
						.with_context(|| format!("failed to read module header `{header}` at {:?}", dir.join(header)))?;
 | 
			
		||||
					let text = std::fs::read_to_string(dir.join(header)).with_context(|| {
 | 
			
		||||
						format!(
 | 
			
		||||
							"failed to read module header `{header}` at {:?}",
 | 
			
		||||
							dir.join(header)
 | 
			
		||||
						)
 | 
			
		||||
					})?;
 | 
			
		||||
 | 
			
		||||
					Ok::<_, anyhow::Error>((header, text))
 | 
			
		||||
				})
 | 
			
		||||
| 
						 | 
				
			
			@ -30,9 +37,11 @@ fn main() -> anyhow::Result<()> {
 | 
			
		|||
 | 
			
		||||
			let parser = parse::Parser::new();
 | 
			
		||||
			let mut ctx = parse::ParseContext::default();
 | 
			
		||||
			texts.iter()
 | 
			
		||||
			texts
 | 
			
		||||
				.iter()
 | 
			
		||||
				.map(|(header, text)| {
 | 
			
		||||
					parser.parse(&text, &mut ctx)
 | 
			
		||||
					parser
 | 
			
		||||
						.parse(&text, &mut ctx)
 | 
			
		||||
						.with_context(|| format!("while parsing module header `{header}`"))
 | 
			
		||||
				})
 | 
			
		||||
				.collect::<Result<_, _>>()?;
 | 
			
		||||
| 
						 | 
				
			
			@ -41,8 +50,7 @@ fn main() -> anyhow::Result<()> {
 | 
			
		|||
 | 
			
		||||
			let text = serde_json::to_string_pretty(&typespec).unwrap();
 | 
			
		||||
 | 
			
		||||
			std::fs::write(outpath, text)
 | 
			
		||||
				.context("saving typespec")?;
 | 
			
		||||
			std::fs::write(outpath, text).context("saving typespec")?;
 | 
			
		||||
		},
 | 
			
		||||
		Some("gendocs") => {
 | 
			
		||||
			let modinfo = args.get(2).expect("expected module file");
 | 
			
		||||
| 
						 | 
				
			
			@ -77,18 +85,24 @@ fn main() -> anyhow::Result<()> {
 | 
			
		|||
			for (name, info) in types {
 | 
			
		||||
				let json = serde_json::to_string_pretty(&info).unwrap();
 | 
			
		||||
				let datapath = datapath.join(format!("{name}.json"));
 | 
			
		||||
				std::fs::write(&datapath, json).with_context(|| format!("while writing {datapath:?}"))?;
 | 
			
		||||
				std::fs::write(&datapath, json)
 | 
			
		||||
					.with_context(|| format!("while writing {datapath:?}"))?;
 | 
			
		||||
 | 
			
		||||
				let template = format!("+++
 | 
			
		||||
				let template = format!(
 | 
			
		||||
					"+++
 | 
			
		||||
title = \"{name}\"
 | 
			
		||||
hidetitle = true
 | 
			
		||||
+++
 | 
			
		||||
 | 
			
		||||
{{{{< qmltype module=\"{module}\" type=\"{name}\" >}}}}
 | 
			
		||||
", name = name, module = module.header.name);
 | 
			
		||||
",
 | 
			
		||||
					name = name,
 | 
			
		||||
					module = module.header.name
 | 
			
		||||
				);
 | 
			
		||||
 | 
			
		||||
				let templatepath = templatepath.join(format!("{name}.md"));
 | 
			
		||||
				std::fs::write(&templatepath, template).with_context(|| format!("while writing {templatepath:?}"))?;
 | 
			
		||||
				std::fs::write(&templatepath, template)
 | 
			
		||||
					.with_context(|| format!("while writing {templatepath:?}"))?;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			let index = outform::ModuleIndex {
 | 
			
		||||
| 
						 | 
				
			
			@ -98,17 +112,22 @@ hidetitle = true
 | 
			
		|||
 | 
			
		||||
			let datapath = datapath.join("index.json");
 | 
			
		||||
			let json = serde_json::to_string_pretty(&index).unwrap();
 | 
			
		||||
			std::fs::write(&datapath, json).with_context(|| format!("while writing {datapath:?}"))?;
 | 
			
		||||
			std::fs::write(&datapath, json)
 | 
			
		||||
				.with_context(|| format!("while writing {datapath:?}"))?;
 | 
			
		||||
 | 
			
		||||
			let template = format!("+++
 | 
			
		||||
			let template = format!(
 | 
			
		||||
				"+++
 | 
			
		||||
title = \"{name}\"
 | 
			
		||||
+++
 | 
			
		||||
 | 
			
		||||
{{{{< qmlmodule module=\"{name}\" >}}}}
 | 
			
		||||
", name = module.header.name);
 | 
			
		||||
",
 | 
			
		||||
				name = module.header.name
 | 
			
		||||
			);
 | 
			
		||||
 | 
			
		||||
			let templatepath = templatepath.join(format!("_index.md"));
 | 
			
		||||
				std::fs::write(&templatepath, template).with_context(|| format!("while writing {templatepath:?}"))?;
 | 
			
		||||
			std::fs::write(&templatepath, template)
 | 
			
		||||
				.with_context(|| format!("while writing {templatepath:?}"))?;
 | 
			
		||||
		},
 | 
			
		||||
		_ => {
 | 
			
		||||
			panic!("typegen invoked without mode");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,7 @@ pub struct ClassInfo {
 | 
			
		|||
	pub flags: Vec<Flag>,
 | 
			
		||||
	pub properties: HashMap<String, Property>,
 | 
			
		||||
	pub functions: HashMap<String, Function>,
 | 
			
		||||
	pub signals: HashMap<String, Signal>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize)]
 | 
			
		||||
| 
						 | 
				
			
			@ -53,6 +54,13 @@ pub struct Function {
 | 
			
		|||
	pub params: HashMap<String, Type>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize)]
 | 
			
		||||
pub struct Signal {
 | 
			
		||||
	pub name: String,
 | 
			
		||||
	pub details: Option<String>,
 | 
			
		||||
	pub params: HashMap<String, Type>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize)]
 | 
			
		||||
pub struct EnumInfo {
 | 
			
		||||
	pub description: Option<String>,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,13 +27,10 @@ pub fn parse_module(text: &str) -> anyhow::Result<ModuleInfo> {
 | 
			
		|||
	header = header.trim();
 | 
			
		||||
	details = details.trim();
 | 
			
		||||
 | 
			
		||||
	let header = toml::from_str::<ModuleInfoHeader>(header)
 | 
			
		||||
    .context("parsing module info header")?;
 | 
			
		||||
	let header =
 | 
			
		||||
		toml::from_str::<ModuleInfoHeader>(header).context("parsing module info header")?;
 | 
			
		||||
 | 
			
		||||
	Ok(ModuleInfo {
 | 
			
		||||
		header,
 | 
			
		||||
		details,
 | 
			
		||||
	})
 | 
			
		||||
	Ok(ModuleInfo { header, details })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
| 
						 | 
				
			
			@ -47,6 +44,7 @@ pub struct ClassInfo<'a> {
 | 
			
		|||
	pub comment: Option<&'a str>,
 | 
			
		||||
	pub properties: Vec<Property<'a>>,
 | 
			
		||||
	pub invokables: Vec<Invokable<'a>>,
 | 
			
		||||
	pub signals: Vec<Signal<'a>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
| 
						 | 
				
			
			@ -73,6 +71,13 @@ pub struct Invokable<'a> {
 | 
			
		|||
	pub params: Vec<InvokableParam<'a>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct Signal<'a> {
 | 
			
		||||
	pub name: &'a str,
 | 
			
		||||
	pub comment: Option<&'a str>,
 | 
			
		||||
	pub params: Vec<InvokableParam<'a>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy)]
 | 
			
		||||
pub struct InvokableParam<'a> {
 | 
			
		||||
	pub name: &'a str,
 | 
			
		||||
| 
						 | 
				
			
			@ -98,6 +103,7 @@ pub struct Parser {
 | 
			
		|||
	pub class_regex: Regex,
 | 
			
		||||
	pub macro_regex: Regex,
 | 
			
		||||
	pub property_regex: Regex,
 | 
			
		||||
	pub signals_regex: Regex,
 | 
			
		||||
	pub fn_regex: Regex,
 | 
			
		||||
	pub fn_param_regex: Regex,
 | 
			
		||||
	pub defaultprop_classinfo_regex: Regex,
 | 
			
		||||
| 
						 | 
				
			
			@ -126,15 +132,20 @@ impl Parser {
 | 
			
		|||
			class_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*class\s+(?<name>\w+)(?:\s*:\s*public\s+(?<super>\w+))?.+?\{(?<body>[\s\S]*?)};"#).unwrap(),
 | 
			
		||||
			macro_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*(?<type>(Q|QML)_\w+)\s*(\(\s*(?<args>.*)\s*\))?;"#).unwrap(),
 | 
			
		||||
			property_regex: Regex::new(r#"^\s*(?<type>(\w|::|<|>)+\*?)\s+(?<name>\w+)(\s+(MEMBER\s+(?<member>\w+)|READ\s+(?<read>\w+)|WRITE\s+(?<write>\w+)|NOTIFY\s+(?<notify>\w+)|(?<const>CONSTANT)))+\s*$"#).unwrap(),
 | 
			
		||||
			fn_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*Q_INVOKABLE\s+(?<type>(\w|::|<|>)+\*?)\s+(?<name>\w+)\((?<params>[\s\S]*?)\);"#).unwrap(),
 | 
			
		||||
			fn_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*(?<invokable>Q_INVOKABLE)?\s+(?<type>(\w|::|<|>)+\*?)\s+(?<name>\w+)\((?<params>[\s\S]*?)\);"#).unwrap(),
 | 
			
		||||
			fn_param_regex: Regex::new(r#"(?<type>(\w|::|<|>)+\*?)\s+(?<name>\w+)(,|$)"#).unwrap(),
 | 
			
		||||
			signals_regex: Regex::new(r#"signals:(?<signals>(\s*(\s*///.*\s*)*void .*;)*)"#).unwrap(),
 | 
			
		||||
			defaultprop_classinfo_regex: Regex::new(r#"^\s*"DefaultProperty", "(?<prop>.+)"\s*$"#).unwrap(),
 | 
			
		||||
			enum_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*namespace (?<namespace>\w+)\s*\{[\s\S]*?(QML_ELEMENT|QML_NAMED_ELEMENT\((?<qml_name>\w+)\));[\s\S]*?enum\s*(?<enum_name>\w+)\s*\{(?<body>[\s\S]*?)\};[\s\S]*?\}"#).unwrap(),
 | 
			
		||||
			enum_variant_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*(?<name>\w+)\s*=\s*\d+,"#).unwrap(),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pub fn parse_classes<'a>(&self, text: &'a str, ctx: &mut ParseContext<'a>) -> anyhow::Result<()> {
 | 
			
		||||
	pub fn parse_classes<'a>(
 | 
			
		||||
		&self,
 | 
			
		||||
		text: &'a str,
 | 
			
		||||
		ctx: &mut ParseContext<'a>,
 | 
			
		||||
	) -> anyhow::Result<()> {
 | 
			
		||||
		for class in self.class_regex.captures_iter(text) {
 | 
			
		||||
			let comment = class.name("comment").map(|m| m.as_str());
 | 
			
		||||
			let name = class.name("name").unwrap().as_str();
 | 
			
		||||
| 
						 | 
				
			
			@ -148,6 +159,8 @@ impl Parser {
 | 
			
		|||
			let mut properties = Vec::new();
 | 
			
		||||
			let mut default_property = None;
 | 
			
		||||
			let mut invokables = Vec::new();
 | 
			
		||||
			let mut notify_signals = Vec::new();
 | 
			
		||||
			let mut signals = Vec::new();
 | 
			
		||||
 | 
			
		||||
			(|| {
 | 
			
		||||
				for macro_ in self.macro_regex.captures_iter(body) {
 | 
			
		||||
| 
						 | 
				
			
			@ -160,11 +173,19 @@ impl Parser {
 | 
			
		|||
							"Q_OBJECT" => classtype = Some(ClassType::Object),
 | 
			
		||||
							"Q_GADGET" => classtype = Some(ClassType::Gadget),
 | 
			
		||||
							"QML_ELEMENT" => qml_name = Some(name),
 | 
			
		||||
							"QML_NAMED_ELEMENT" => qml_name = Some(args.ok_or_else(|| anyhow!("expected name for QML_NAMED_ELEMENT"))?),
 | 
			
		||||
							"QML_NAMED_ELEMENT" => {
 | 
			
		||||
								qml_name = Some(args.ok_or_else(|| {
 | 
			
		||||
									anyhow!("expected name for QML_NAMED_ELEMENT")
 | 
			
		||||
								})?)
 | 
			
		||||
							},
 | 
			
		||||
							"QML_SINGLETON" => singleton = true,
 | 
			
		||||
							"QML_UNCREATABLE" => uncreatable = true,
 | 
			
		||||
							"Q_PROPERTY" => {
 | 
			
		||||
								let prop = self.property_regex.captures(args.ok_or_else(|| anyhow!("expected args for Q_PROPERTY"))?)
 | 
			
		||||
								let prop =
 | 
			
		||||
									self.property_regex
 | 
			
		||||
										.captures(args.ok_or_else(|| {
 | 
			
		||||
											anyhow!("expected args for Q_PROPERTY")
 | 
			
		||||
										})?)
 | 
			
		||||
										.ok_or_else(|| anyhow!("unable to parse Q_PROPERTY"))?;
 | 
			
		||||
 | 
			
		||||
								let member = prop.name("member").is_some();
 | 
			
		||||
| 
						 | 
				
			
			@ -172,6 +193,10 @@ impl Parser {
 | 
			
		|||
								let write = prop.name("write").is_some();
 | 
			
		||||
								let constant = prop.name("const").is_some();
 | 
			
		||||
 | 
			
		||||
								if let Some(notify) = prop.name("notify").map(|v| v.as_str()) {
 | 
			
		||||
									notify_signals.push(notify);
 | 
			
		||||
								}
 | 
			
		||||
 | 
			
		||||
								properties.push(Property {
 | 
			
		||||
									type_: prop.name("type").unwrap().as_str(),
 | 
			
		||||
									name: prop.name("name").unwrap().as_str(),
 | 
			
		||||
| 
						 | 
				
			
			@ -182,7 +207,9 @@ impl Parser {
 | 
			
		|||
								});
 | 
			
		||||
							},
 | 
			
		||||
							"Q_CLASSINFO" => {
 | 
			
		||||
								let classinfo = self.defaultprop_classinfo_regex.captures(args.ok_or_else(|| anyhow!("expected args for Q_CLASSINFO"))?);
 | 
			
		||||
								let classinfo = self.defaultprop_classinfo_regex.captures(
 | 
			
		||||
									args.ok_or_else(|| anyhow!("expected args for Q_CLASSINFO"))?,
 | 
			
		||||
								);
 | 
			
		||||
 | 
			
		||||
								if let Some(classinfo) = classinfo {
 | 
			
		||||
									let prop = classinfo.name("prop").unwrap().as_str();
 | 
			
		||||
| 
						 | 
				
			
			@ -192,10 +219,26 @@ impl Parser {
 | 
			
		|||
							_ => {},
 | 
			
		||||
						}
 | 
			
		||||
						Ok::<_, anyhow::Error>(())
 | 
			
		||||
					})().with_context(|| format!("while parsing macro `{}`", macro_.get(0).unwrap().as_str()))?;
 | 
			
		||||
					})()
 | 
			
		||||
					.with_context(|| {
 | 
			
		||||
						format!("while parsing macro `{}`", macro_.get(0).unwrap().as_str())
 | 
			
		||||
					})?;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if let Some(prop) = default_property {
 | 
			
		||||
					let prop = properties
 | 
			
		||||
						.iter_mut()
 | 
			
		||||
						.find(|p| p.name == prop)
 | 
			
		||||
						.ok_or_else(|| anyhow!("could not find default property `{prop}`"))?;
 | 
			
		||||
 | 
			
		||||
					prop.default = true;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				for invokable in self.fn_regex.captures_iter(body) {
 | 
			
		||||
					if invokable.name("invokable").is_none() {
 | 
			
		||||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					let comment = invokable.name("comment").map(|m| m.as_str());
 | 
			
		||||
					let type_ = invokable.name("type").unwrap().as_str();
 | 
			
		||||
					let name = invokable.name("name").unwrap().as_str();
 | 
			
		||||
| 
						 | 
				
			
			@ -207,10 +250,7 @@ impl Parser {
 | 
			
		|||
						let type_ = param.name("type").unwrap().as_str();
 | 
			
		||||
						let name = param.name("name").unwrap().as_str();
 | 
			
		||||
 | 
			
		||||
						params.push(InvokableParam {
 | 
			
		||||
							type_,
 | 
			
		||||
							name,
 | 
			
		||||
						});
 | 
			
		||||
						params.push(InvokableParam { type_, name });
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					invokables.push(Invokable {
 | 
			
		||||
| 
						 | 
				
			
			@ -221,15 +261,47 @@ impl Parser {
 | 
			
		|||
					});
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if let Some(prop) = default_property {
 | 
			
		||||
					let prop = properties.iter_mut().find(|p| p.name == prop)
 | 
			
		||||
						.ok_or_else(|| anyhow!("could not find default property `{prop}`"))?;
 | 
			
		||||
				for signal_set in self.signals_regex.captures_iter(body) {
 | 
			
		||||
					let signals_body = signal_set.name("signals").unwrap().as_str();
 | 
			
		||||
 | 
			
		||||
					prop.default = true;
 | 
			
		||||
					for signal in self.fn_regex.captures_iter(signals_body) {
 | 
			
		||||
						if signal.name("invokable").is_some() {
 | 
			
		||||
							continue;
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						let comment = signal.name("comment").map(|m| m.as_str());
 | 
			
		||||
						let type_ = signal.name("type").unwrap().as_str();
 | 
			
		||||
						let name = signal.name("name").unwrap().as_str();
 | 
			
		||||
						let params_raw = signal.name("params").unwrap().as_str();
 | 
			
		||||
 | 
			
		||||
						if notify_signals.contains(&name) {
 | 
			
		||||
							continue;
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						if type_ != "void" {
 | 
			
		||||
							bail!("non void return for signal {name}");
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						let mut params = Vec::new();
 | 
			
		||||
 | 
			
		||||
						for param in self.fn_param_regex.captures_iter(params_raw) {
 | 
			
		||||
							let type_ = param.name("type").unwrap().as_str();
 | 
			
		||||
							let name = param.name("name").unwrap().as_str();
 | 
			
		||||
 | 
			
		||||
							params.push(InvokableParam { type_, name });
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						signals.push(Signal {
 | 
			
		||||
							name,
 | 
			
		||||
							comment,
 | 
			
		||||
							params,
 | 
			
		||||
						});
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				Ok::<_, anyhow::Error>(())
 | 
			
		||||
			})().with_context(|| format!("while parsing class `{name}`"))?;
 | 
			
		||||
			})()
 | 
			
		||||
			.with_context(|| format!("while parsing class `{name}`"))?;
 | 
			
		||||
 | 
			
		||||
			let Some(type_) = classtype else { continue };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -243,6 +315,7 @@ impl Parser {
 | 
			
		|||
				comment,
 | 
			
		||||
				properties,
 | 
			
		||||
				invokables,
 | 
			
		||||
				signals,
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -254,7 +327,10 @@ impl Parser {
 | 
			
		|||
			let comment = enum_.name("comment").map(|m| m.as_str());
 | 
			
		||||
			let namespace = enum_.name("namespace").unwrap().as_str();
 | 
			
		||||
			let enum_name = enum_.name("enum_name").unwrap().as_str();
 | 
			
		||||
			let qml_name = enum_.name("qml_name").map(|m| m.as_str()).unwrap_or(namespace);
 | 
			
		||||
			let qml_name = enum_
 | 
			
		||||
				.name("qml_name")
 | 
			
		||||
				.map(|m| m.as_str())
 | 
			
		||||
				.unwrap_or(namespace);
 | 
			
		||||
			let body = enum_.name("body").unwrap().as_str();
 | 
			
		||||
 | 
			
		||||
			let mut variants = Vec::new();
 | 
			
		||||
| 
						 | 
				
			
			@ -263,10 +339,7 @@ impl Parser {
 | 
			
		|||
				let comment = variant.name("comment").map(|m| m.as_str());
 | 
			
		||||
				let name = variant.name("name").unwrap().as_str();
 | 
			
		||||
 | 
			
		||||
				variants.push(Variant {
 | 
			
		||||
					name,
 | 
			
		||||
					comment,
 | 
			
		||||
				});
 | 
			
		||||
				variants.push(Variant { name, comment });
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ctx.enums.push(EnumInfo {
 | 
			
		||||
| 
						 | 
				
			
			@ -292,16 +365,25 @@ impl Parser {
 | 
			
		|||
impl ParseContext<'_> {
 | 
			
		||||
	pub fn gen_typespec(&self, module: &str) -> typespec::TypeSpec {
 | 
			
		||||
		typespec::TypeSpec {
 | 
			
		||||
			typemap: self.classes.iter().filter_map(|class| {
 | 
			
		||||
			typemap: self
 | 
			
		||||
				.classes
 | 
			
		||||
				.iter()
 | 
			
		||||
				.filter_map(|class| {
 | 
			
		||||
					Some(typespec::QmlTypeMapping {
 | 
			
		||||
						// filters gadgets
 | 
			
		||||
						name: class.qml_name?.to_string(),
 | 
			
		||||
						cname: class.name.to_string(),
 | 
			
		||||
						module: Some(module.to_string()),
 | 
			
		||||
					})
 | 
			
		||||
			}).collect(),
 | 
			
		||||
			classes: self.classes.iter().filter_map(|class| {
 | 
			
		||||
				let (description, details) = class.comment.map(parse_details_desc)
 | 
			
		||||
				})
 | 
			
		||||
				.collect(),
 | 
			
		||||
			classes: self
 | 
			
		||||
				.classes
 | 
			
		||||
				.iter()
 | 
			
		||||
				.filter_map(|class| {
 | 
			
		||||
					let (description, details) = class
 | 
			
		||||
						.comment
 | 
			
		||||
						.map(parse_details_desc)
 | 
			
		||||
						.unwrap_or((None, None));
 | 
			
		||||
 | 
			
		||||
					Some(typespec::Class {
 | 
			
		||||
| 
						 | 
				
			
			@ -315,17 +397,28 @@ impl ParseContext<'_> {
 | 
			
		|||
						uncreatable: class.uncreatable,
 | 
			
		||||
						properties: class.properties.iter().map(|p| (*p).into()).collect(),
 | 
			
		||||
						functions: class.invokables.iter().map(|f| f.as_typespec()).collect(),
 | 
			
		||||
						signals: class.signals.iter().map(|s| s.as_typespec()).collect(),
 | 
			
		||||
					})
 | 
			
		||||
			}).collect(),
 | 
			
		||||
			gadgets: self.classes.iter().filter_map(|class| match class.type_ {
 | 
			
		||||
				})
 | 
			
		||||
				.collect(),
 | 
			
		||||
			gadgets: self
 | 
			
		||||
				.classes
 | 
			
		||||
				.iter()
 | 
			
		||||
				.filter_map(|class| match class.type_ {
 | 
			
		||||
					ClassType::Gadget => Some(typespec::Gadget {
 | 
			
		||||
						cname: class.name.to_string(),
 | 
			
		||||
						properties: class.properties.iter().map(|p| (*p).into()).collect(),
 | 
			
		||||
					}),
 | 
			
		||||
					_ => None,
 | 
			
		||||
			}).collect(),
 | 
			
		||||
			enums: self.enums.iter().map(|enum_| {
 | 
			
		||||
				let (description, details) = enum_.comment.map(parse_details_desc)
 | 
			
		||||
				})
 | 
			
		||||
				.collect(),
 | 
			
		||||
			enums: self
 | 
			
		||||
				.enums
 | 
			
		||||
				.iter()
 | 
			
		||||
				.map(|enum_| {
 | 
			
		||||
					let (description, details) = enum_
 | 
			
		||||
						.comment
 | 
			
		||||
						.map(parse_details_desc)
 | 
			
		||||
						.unwrap_or((None, None));
 | 
			
		||||
 | 
			
		||||
					typespec::Enum {
 | 
			
		||||
| 
						 | 
				
			
			@ -336,7 +429,8 @@ impl ParseContext<'_> {
 | 
			
		|||
						details,
 | 
			
		||||
						varaints: enum_.variants.iter().map(|v| (*v).into()).collect(),
 | 
			
		||||
					}
 | 
			
		||||
			}).collect(),
 | 
			
		||||
				})
 | 
			
		||||
				.collect(),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -374,6 +468,16 @@ impl Invokable<'_> {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Signal<'_> {
 | 
			
		||||
	fn as_typespec(&self) -> typespec::Signal {
 | 
			
		||||
		typespec::Signal {
 | 
			
		||||
			name: self.name.to_string(),
 | 
			
		||||
			details: self.comment.map(parse_details),
 | 
			
		||||
			params: self.params.iter().map(|p| (*p).into()).collect(),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<InvokableParam<'_>> for typespec::FnParam {
 | 
			
		||||
	fn from(value: InvokableParam<'_>) -> Self {
 | 
			
		||||
		Self {
 | 
			
		||||
| 
						 | 
				
			
			@ -387,7 +491,8 @@ fn parse_details(text: &str) -> String {
 | 
			
		|||
	let mut seen_content = false;
 | 
			
		||||
	let mut callout = false;
 | 
			
		||||
 | 
			
		||||
	let mut str = text.lines()
 | 
			
		||||
	let mut str = text
 | 
			
		||||
		.lines()
 | 
			
		||||
		.map(|line| {
 | 
			
		||||
			line.trim()
 | 
			
		||||
				.strip_prefix("///")
 | 
			
		||||
| 
						 | 
				
			
			@ -400,8 +505,7 @@ fn parse_details(text: &str) -> String {
 | 
			
		|||
			seen_content |= any;
 | 
			
		||||
			filter
 | 
			
		||||
		})
 | 
			
		||||
    .map(|line| {
 | 
			
		||||
			match callout {
 | 
			
		||||
		.map(|line| match callout {
 | 
			
		||||
			true => {
 | 
			
		||||
				if line.starts_with('>') {
 | 
			
		||||
					Cow::Borrowed(line[1..].strip_prefix(' ').unwrap_or(&line[1..]))
 | 
			
		||||
| 
						 | 
				
			
			@ -409,7 +513,7 @@ fn parse_details(text: &str) -> String {
 | 
			
		|||
					callout = false;
 | 
			
		||||
					Cow::Owned(format!("{{{{< /callout >}}}}\n{line}"))
 | 
			
		||||
				}
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			false => {
 | 
			
		||||
				if line.starts_with("> [!") {
 | 
			
		||||
					let code = line[4..].split_once(']');
 | 
			
		||||
| 
						 | 
				
			
			@ -422,8 +526,7 @@ fn parse_details(text: &str) -> String {
 | 
			
		|||
				}
 | 
			
		||||
 | 
			
		||||
				return Cow::Borrowed(line);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			},
 | 
			
		||||
		})
 | 
			
		||||
		.fold(String::new(), |accum, line| accum + line.as_ref() + "\n");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -438,8 +541,19 @@ fn parse_details_desc(text: &str) -> (Option<String>, Option<String>) {
 | 
			
		|||
	let details = parse_details(text);
 | 
			
		||||
	if details.starts_with('!') {
 | 
			
		||||
		match details[1..].split_once('\n') {
 | 
			
		||||
			Some((desc, details)) => (Some(desc.strip_prefix(' ').unwrap_or(desc).to_string()), Some(details.to_string())),
 | 
			
		||||
			None => (Some(details[1..].strip_prefix(' ').unwrap_or(&details[1..]).to_string()), None),
 | 
			
		||||
			Some((desc, details)) => (
 | 
			
		||||
				Some(desc.strip_prefix(' ').unwrap_or(desc).to_string()),
 | 
			
		||||
				Some(details.to_string()),
 | 
			
		||||
			),
 | 
			
		||||
			None => (
 | 
			
		||||
				Some(
 | 
			
		||||
					details[1..]
 | 
			
		||||
						.strip_prefix(' ')
 | 
			
		||||
						.unwrap_or(&details[1..])
 | 
			
		||||
						.to_string(),
 | 
			
		||||
				),
 | 
			
		||||
				None,
 | 
			
		||||
			),
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		(None, Some(details))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,24 +1,35 @@
 | 
			
		|||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
use crate::{outform::{self, Flag, PropertyType}, typespec::{FnParam, Function, Property, TypeSpec}};
 | 
			
		||||
use crate::{
 | 
			
		||||
	outform::{self, Flag, PropertyType},
 | 
			
		||||
	typespec::{FnParam, Function, Property, Signal, TypeSpec},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub fn resolve_types(module: &str, typespec: TypeSpec) -> anyhow::Result<HashMap<String, outform::TypeInfo>> {
 | 
			
		||||
pub fn resolve_types(
 | 
			
		||||
	module: &str,
 | 
			
		||||
	typespec: TypeSpec,
 | 
			
		||||
) -> anyhow::Result<HashMap<String, outform::TypeInfo>> {
 | 
			
		||||
	let mut outtypes = HashMap::new();
 | 
			
		||||
 | 
			
		||||
	let types = typespec.typemap.iter()
 | 
			
		||||
	let types = typespec
 | 
			
		||||
		.typemap
 | 
			
		||||
		.iter()
 | 
			
		||||
		.filter(|type_| type_.module.as_ref().map(|v| v as &str) == Some(module));
 | 
			
		||||
 | 
			
		||||
	let findqmltype = |cname: &str| typespec.typemap
 | 
			
		||||
		.iter()
 | 
			
		||||
		.find(|type_| type_.cname == cname);
 | 
			
		||||
	let findqmltype = |cname: &str| typespec.typemap.iter().find(|type_| type_.cname == cname);
 | 
			
		||||
 | 
			
		||||
	for mapping in types {
 | 
			
		||||
		let Some(class) = typespec.classes.iter().find(|class| class.name == mapping.cname) else {
 | 
			
		||||
		let Some(class) = typespec
 | 
			
		||||
			.classes
 | 
			
		||||
			.iter()
 | 
			
		||||
			.find(|class| class.name == mapping.cname)
 | 
			
		||||
		else {
 | 
			
		||||
			continue
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		let mut properties = Vec::<&Property>::new();
 | 
			
		||||
		let mut functions = Vec::<&Function>::new();
 | 
			
		||||
		let mut signals = Vec::<&Signal>::new();
 | 
			
		||||
 | 
			
		||||
		// the first superclass availible from QML
 | 
			
		||||
		let mut superclass = &class.superclass;
 | 
			
		||||
| 
						 | 
				
			
			@ -29,12 +40,16 @@ pub fn resolve_types(module: &str, typespec: TypeSpec) -> anyhow::Result<HashMap
 | 
			
		|||
				break outform::Type::resolve(type_.module.as_ref().map(|v| v as &str), &type_.name)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			let superctype = typespec.classes.iter().find(|class| &class.name == superclass);
 | 
			
		||||
			let superctype = typespec
 | 
			
		||||
				.classes
 | 
			
		||||
				.iter()
 | 
			
		||||
				.find(|class| &class.name == superclass);
 | 
			
		||||
 | 
			
		||||
			match superctype {
 | 
			
		||||
				Some(superctype) => {
 | 
			
		||||
					properties.extend(superctype.properties.iter());
 | 
			
		||||
					functions.extend(superctype.functions.iter());
 | 
			
		||||
					signals.extend(superctype.signals.iter());
 | 
			
		||||
					superclass = &superctype.superclass;
 | 
			
		||||
				},
 | 
			
		||||
				None => break outform::Type::unknown(),
 | 
			
		||||
| 
						 | 
				
			
			@ -42,19 +57,23 @@ pub fn resolve_types(module: &str, typespec: TypeSpec) -> anyhow::Result<HashMap
 | 
			
		|||
		};
 | 
			
		||||
 | 
			
		||||
		fn qmlparamtype(ctype: &str, typespec: &TypeSpec) -> outform::Type {
 | 
			
		||||
			let qtype = typespec.typemap
 | 
			
		||||
			let qtype = typespec
 | 
			
		||||
				.typemap
 | 
			
		||||
				.iter()
 | 
			
		||||
				.find(|type_| &type_.cname == ctype)
 | 
			
		||||
				.map(|type_| (&type_.module, &type_.name))
 | 
			
		||||
				.or_else(|| {
 | 
			
		||||
					typespec.enums
 | 
			
		||||
					typespec
 | 
			
		||||
						.enums
 | 
			
		||||
						.iter()
 | 
			
		||||
						.find(|type_| type_.cname.as_ref().map(|v| v as &str) == Some(ctype))
 | 
			
		||||
						.map(|type_| (&type_.module, &type_.name))
 | 
			
		||||
				});
 | 
			
		||||
 | 
			
		||||
			match qtype {
 | 
			
		||||
				Some((module, name)) => outform::Type::resolve(module.as_ref().map(|v| v as &str), &name),
 | 
			
		||||
				Some((module, name)) => {
 | 
			
		||||
					outform::Type::resolve(module.as_ref().map(|v| v as &str), &name)
 | 
			
		||||
				},
 | 
			
		||||
				None => outform::Type::unknown(),
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -78,15 +97,16 @@ pub fn resolve_types(module: &str, typespec: TypeSpec) -> anyhow::Result<HashMap
 | 
			
		|||
				flags
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			let gadget = typespec.gadgets.iter()
 | 
			
		||||
				.find(|gadget| gadget.cname == ctype);
 | 
			
		||||
			let gadget = typespec.gadgets.iter().find(|gadget| gadget.cname == ctype);
 | 
			
		||||
 | 
			
		||||
			match gadget {
 | 
			
		||||
				Some(gadget) => outform::Property {
 | 
			
		||||
					type_: PropertyType::Gadget(
 | 
			
		||||
						gadget.properties.iter()
 | 
			
		||||
						gadget
 | 
			
		||||
							.properties
 | 
			
		||||
							.iter()
 | 
			
		||||
							.map(|prop| (prop.name.clone(), solveprop(prop, typespec).type_))
 | 
			
		||||
							.collect()
 | 
			
		||||
							.collect(),
 | 
			
		||||
					),
 | 
			
		||||
					details: prop.details.clone(),
 | 
			
		||||
					flags,
 | 
			
		||||
| 
						 | 
				
			
			@ -131,7 +151,23 @@ pub fn resolve_types(module: &str, typespec: TypeSpec) -> anyhow::Result<HashMap
 | 
			
		|||
				ret: qmlparamtype(&func.ret, typespec),
 | 
			
		||||
				name: func.name.clone(),
 | 
			
		||||
				details: func.details.clone(),
 | 
			
		||||
				params: func.params.iter().map(|FnParam { type_, name }| (name.clone(), qmlparamtype(type_, typespec))).collect(),
 | 
			
		||||
				params: func
 | 
			
		||||
					.params
 | 
			
		||||
					.iter()
 | 
			
		||||
					.map(|FnParam { type_, name }| (name.clone(), qmlparamtype(type_, typespec)))
 | 
			
		||||
					.collect(),
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fn solvesignal(func: &Signal, typespec: &TypeSpec) -> outform::Signal {
 | 
			
		||||
			outform::Signal {
 | 
			
		||||
				name: func.name.clone(),
 | 
			
		||||
				details: func.details.clone(),
 | 
			
		||||
				params: func
 | 
			
		||||
					.params
 | 
			
		||||
					.iter()
 | 
			
		||||
					.map(|FnParam { type_, name }| (name.clone(), qmlparamtype(type_, typespec)))
 | 
			
		||||
					.collect(),
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -141,15 +177,23 @@ pub fn resolve_types(module: &str, typespec: TypeSpec) -> anyhow::Result<HashMap
 | 
			
		|||
		functions.extend(class.functions.iter());
 | 
			
		||||
		functions.sort_by(|a, b| Ord::cmp(&a.name, &b.name));
 | 
			
		||||
 | 
			
		||||
		let properties = properties.iter().map(|prop| (
 | 
			
		||||
			prop.name.clone(),
 | 
			
		||||
			solveprop(prop, &typespec)
 | 
			
		||||
		)).collect::<HashMap<_, _>>();
 | 
			
		||||
		signals.extend(class.signals.iter());
 | 
			
		||||
		signals.sort_by(|a, b| Ord::cmp(&a.name, &b.name));
 | 
			
		||||
 | 
			
		||||
		let functions = functions.iter().map(|func| (
 | 
			
		||||
			func.name.clone(),
 | 
			
		||||
			solvefunc(func, &typespec)
 | 
			
		||||
		)).collect::<HashMap<_, _>>();
 | 
			
		||||
		let properties = properties
 | 
			
		||||
			.iter()
 | 
			
		||||
			.map(|prop| (prop.name.clone(), solveprop(prop, &typespec)))
 | 
			
		||||
			.collect::<HashMap<_, _>>();
 | 
			
		||||
 | 
			
		||||
		let functions = functions
 | 
			
		||||
			.iter()
 | 
			
		||||
			.map(|func| (func.name.clone(), solvefunc(func, &typespec)))
 | 
			
		||||
			.collect::<HashMap<_, _>>();
 | 
			
		||||
 | 
			
		||||
		let signals = signals
 | 
			
		||||
			.iter()
 | 
			
		||||
			.map(|signal| (signal.name.clone(), solvesignal(signal, &typespec)))
 | 
			
		||||
			.collect::<HashMap<_, _>>();
 | 
			
		||||
 | 
			
		||||
		let type_ = outform::ClassInfo {
 | 
			
		||||
			superclass,
 | 
			
		||||
| 
						 | 
				
			
			@ -168,6 +212,7 @@ pub fn resolve_types(module: &str, typespec: TypeSpec) -> anyhow::Result<HashMap
 | 
			
		|||
			},
 | 
			
		||||
			properties,
 | 
			
		||||
			functions,
 | 
			
		||||
			signals,
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		outtypes.insert(mapping.name.clone(), outform::TypeInfo::Class(type_));
 | 
			
		||||
| 
						 | 
				
			
			@ -175,16 +220,22 @@ pub fn resolve_types(module: &str, typespec: TypeSpec) -> anyhow::Result<HashMap
 | 
			
		|||
 | 
			
		||||
	for enum_ in typespec.enums {
 | 
			
		||||
		if enum_.module.as_ref().map(|v| v as &str) == Some(module) {
 | 
			
		||||
			outtypes.insert(enum_.name, outform::TypeInfo::Enum(outform::EnumInfo {
 | 
			
		||||
			outtypes.insert(
 | 
			
		||||
				enum_.name,
 | 
			
		||||
				outform::TypeInfo::Enum(outform::EnumInfo {
 | 
			
		||||
					description: enum_.description,
 | 
			
		||||
					details: enum_.details,
 | 
			
		||||
				variants: enum_.varaints.into_iter().map(|variant| (
 | 
			
		||||
					variant.name,
 | 
			
		||||
					outform::Variant {
 | 
			
		||||
					variants: enum_
 | 
			
		||||
						.varaints
 | 
			
		||||
						.into_iter()
 | 
			
		||||
						.map(|variant| {
 | 
			
		||||
							(variant.name, outform::Variant {
 | 
			
		||||
								details: variant.details,
 | 
			
		||||
					},
 | 
			
		||||
				)).collect(),
 | 
			
		||||
			}));
 | 
			
		||||
							})
 | 
			
		||||
						})
 | 
			
		||||
						.collect(),
 | 
			
		||||
				}),
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,7 @@ pub struct Class {
 | 
			
		|||
	pub uncreatable: bool,
 | 
			
		||||
	pub properties: Vec<Property>,
 | 
			
		||||
	pub functions: Vec<Function>,
 | 
			
		||||
	pub signals: Vec<Signal>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize)]
 | 
			
		||||
| 
						 | 
				
			
			@ -61,7 +62,14 @@ pub struct Function {
 | 
			
		|||
	pub ret: String,
 | 
			
		||||
	pub name: String,
 | 
			
		||||
	pub details: Option<String>,
 | 
			
		||||
	pub params: Vec<FnParam>
 | 
			
		||||
	pub params: Vec<FnParam>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize)]
 | 
			
		||||
pub struct Signal {
 | 
			
		||||
	pub name: String,
 | 
			
		||||
	pub details: Option<String>,
 | 
			
		||||
	pub params: Vec<FnParam>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize)]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue