diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 938a2d319b..b14b8e88b8 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -32,6 +32,7 @@ jobs: >> $env:GITHUB_ENV - name: Run cargo clippy run: | + cargo clippy -p riddle -- -A clippy::uninlined_format_args && cargo clippy -p sample_bits -- -A clippy::uninlined_format_args && cargo clippy -p sample_com_uri -- -A clippy::uninlined_format_args && cargo clippy -p sample_consent -- -A clippy::uninlined_format_args && @@ -102,6 +103,7 @@ jobs: cargo clippy -p test_reserved -- -A clippy::uninlined_format_args && cargo clippy -p test_resources -- -A clippy::uninlined_format_args && cargo clippy -p test_return_struct -- -A clippy::uninlined_format_args && + cargo clippy -p test_riddle -- -A clippy::uninlined_format_args && cargo clippy -p test_simple_component -- -A clippy::uninlined_format_args && cargo clippy -p test_string_param -- -A clippy::uninlined_format_args && cargo clippy -p test_structs -- -A clippy::uninlined_format_args && diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 54454fe593..90efcc1d4f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -82,6 +82,7 @@ jobs: } - name: Test run: > + cargo test --target ${{ matrix.target }} -p riddle && cargo test --target ${{ matrix.target }} -p sample_bits && cargo test --target ${{ matrix.target }} -p sample_com_uri && cargo test --target ${{ matrix.target }} -p sample_consent && @@ -152,6 +153,7 @@ jobs: cargo test --target ${{ matrix.target }} -p test_reserved && cargo test --target ${{ matrix.target }} -p test_resources && cargo test --target ${{ matrix.target }} -p test_return_struct && + cargo test --target ${{ matrix.target }} -p test_riddle && cargo test --target ${{ matrix.target }} -p test_simple_component && cargo test --target ${{ matrix.target }} -p test_string_param && cargo test --target ${{ matrix.target }} -p test_structs && diff --git a/crates/libs/metadata/src/flags.rs b/crates/libs/metadata/src/attributes.rs similarity index 52% rename from crates/libs/metadata/src/flags.rs rename to crates/libs/metadata/src/attributes.rs index 84f3154215..40b6326fed 100644 --- a/crates/libs/metadata/src/flags.rs +++ b/crates/libs/metadata/src/attributes.rs @@ -1,42 +1,4 @@ -macro_rules! flags { - ($name:ident, $size:ty) => { - #[derive(Default, Copy, Clone, PartialEq, Eq)] - pub struct $name(pub $size); - impl $name { - pub fn contains(&self, contains: Self) -> bool { - *self & contains == contains - } - } - impl std::ops::BitOr for $name { - type Output = Self; - fn bitor(self, other: Self) -> Self { - Self(self.0 | other.0) - } - } - impl std::ops::BitAnd for $name { - type Output = Self; - fn bitand(self, other: Self) -> Self { - Self(self.0 & other.0) - } - } - impl std::ops::BitOrAssign for $name { - fn bitor_assign(&mut self, other: Self) { - self.0.bitor_assign(other.0) - } - } - impl std::ops::BitAndAssign for $name { - fn bitand_assign(&mut self, other: Self) { - self.0.bitand_assign(other.0) - } - } - impl std::ops::Not for $name { - type Output = Self; - fn not(self) -> Self { - Self(self.0.not()) - } - } - }; -} +use super::*; flags!(FieldAttributes, u16); impl FieldAttributes { @@ -49,9 +11,14 @@ impl FieldAttributes { pub const HAS_DEFAULT: Self = Self(0x8000); } -flags!(MethodAttributes, usize); +flags!(MethodAttributes, u16); impl MethodAttributes { + pub const ABSTRACT: Self = Self(0x400); + pub const HIDE_BY_SIG: Self = Self(0x80); + pub const NEW_SLOT: Self = Self(0x100); + pub const PUBLIC: Self = Self(0x6); pub const SPECIAL: Self = Self(0x800); + pub const VIRTUAL: Self = Self(0x40); } flags!(MethodImplAttributes, usize); @@ -59,7 +26,7 @@ impl MethodImplAttributes { pub const PRESERVE_SIG: Self = Self(0x80); } -flags!(ParamAttributes, usize); +flags!(ParamAttributes, u16); impl ParamAttributes { pub const INPUT: Self = Self(0x1); pub const OUTPUT: Self = Self(0x2); @@ -84,4 +51,5 @@ impl TypeAttributes { pub const SEALED: Self = Self(0x100); pub const WINRT: Self = Self(0x4000); pub const INTERFACE: Self = Self(0x20); + pub const SEQUENTIAL_LAYOUT: Self = Self(0x8); } diff --git a/crates/libs/metadata/src/lib.rs b/crates/libs/metadata/src/lib.rs index e288e07b86..0e832603ca 100644 --- a/crates/libs/metadata/src/lib.rs +++ b/crates/libs/metadata/src/lib.rs @@ -1,15 +1,57 @@ #![allow(dead_code)] use std::collections::*; +mod attributes; mod bindings; -mod flags; mod imp; pub mod reader; pub mod writer; +pub use attributes::*; use bindings::*; -pub use flags::*; use imp::*; use std::io::*; use std::mem::*; use std::ptr::*; + +macro_rules! flags { + ($name:ident, $size:ty) => { + #[derive(Default, Copy, Clone, PartialEq, Eq)] + pub struct $name(pub $size); + impl $name { + pub fn contains(&self, contains: Self) -> bool { + *self & contains == contains + } + } + impl std::ops::BitOr for $name { + type Output = Self; + fn bitor(self, other: Self) -> Self { + Self(self.0 | other.0) + } + } + impl std::ops::BitAnd for $name { + type Output = Self; + fn bitand(self, other: Self) -> Self { + Self(self.0 & other.0) + } + } + impl std::ops::BitOrAssign for $name { + fn bitor_assign(&mut self, other: Self) { + self.0.bitor_assign(other.0) + } + } + impl std::ops::BitAndAssign for $name { + fn bitand_assign(&mut self, other: Self) { + self.0.bitand_assign(other.0) + } + } + impl std::ops::Not for $name { + type Output = Self; + fn not(self) -> Self { + Self(self.0.not()) + } + } + }; +} + +pub(crate) use flags; diff --git a/crates/libs/metadata/src/reader/mod.rs b/crates/libs/metadata/src/reader/mod.rs index 7870c7541c..2da30e90a7 100644 --- a/crates/libs/metadata/src/reader/mod.rs +++ b/crates/libs/metadata/src/reader/mod.rs @@ -490,7 +490,7 @@ impl<'a> Reader<'a> { MethodImplAttributes(self.row_usize(row.0, 1)) } pub fn method_def_flags(&self, row: MethodDef) -> MethodAttributes { - MethodAttributes(self.row_usize(row.0, 2)) + MethodAttributes(self.row_usize(row.0, 2) as _) } pub fn method_def_name(&self, row: MethodDef) -> &str { self.row_str(row.0, 3) @@ -727,7 +727,7 @@ impl<'a> Reader<'a> { // pub fn param_flags(&self, row: Param) -> ParamAttributes { - ParamAttributes(self.row_usize(row.0, 0)) + ParamAttributes(self.row_usize(row.0, 0) as _) } pub fn param_sequence(&self, row: Param) -> usize { self.row_usize(row.0, 1) diff --git a/crates/libs/metadata/src/writer/imp/mod.rs b/crates/libs/metadata/src/writer/imp/mod.rs index f04b447e33..d4a9182dd7 100644 --- a/crates/libs/metadata/src/writer/imp/mod.rs +++ b/crates/libs/metadata/src/writer/imp/mod.rs @@ -23,7 +23,7 @@ pub fn round(size: usize, round: usize) -> usize { pub fn write(name: &str, winrt: bool, definitions: &[Item], assemblies: &[&str]) -> Vec { // Index assemblies used to resolve references to existing winmd files. - let assemblies = reader::File::with_default(assemblies).expect("Assemblies could not be loaded"); + let assemblies: Vec = assemblies.iter().map(|file| reader::File::new(file).expect("Assemblies could not be loaded")).collect(); let assemblies = &reader::Reader::new(&assemblies); // Build sorted list of definitions. @@ -82,6 +82,13 @@ pub fn write(name: &str, winrt: bool, definitions: &[Item], assemblies: &[&str]) Item::Interface(ty) => { strings.insert(&ty.namespace); strings.insert(&ty.name); + ty.methods.iter().for_each(|method| { + strings.insert(&method.name); + blobs.insert(method_blob(method, definitions, references)); + method.params.iter().for_each(|param| { + strings.insert(¶m.name); + }); + }); } } } @@ -101,7 +108,7 @@ pub fn write(name: &str, winrt: bool, definitions: &[Item], assemblies: &[&str]) for (_index, item) in definitions.iter() { match item { Item::Struct(ty) => { - let mut flags = TypeAttributes::PUBLIC; + let mut flags = TypeAttributes::PUBLIC | TypeAttributes::SEQUENTIAL_LAYOUT | TypeAttributes::SEALED; if winrt { flags |= TypeAttributes::WINRT; } @@ -111,7 +118,7 @@ pub fn write(name: &str, winrt: bool, definitions: &[Item], assemblies: &[&str]) TypeNamespace: strings.index(&ty.namespace), Extends: TypeDefOrRef::TypeRef(value_type).encode(), FieldList: tables.Field.len() as _, - MethodList: 0, + MethodList: tables.MethodDef.len() as _, }); for field in &ty.fields { let flags = FieldAttributes::PUBLIC; @@ -129,7 +136,7 @@ pub fn write(name: &str, winrt: bool, definitions: &[Item], assemblies: &[&str]) TypeNamespace: strings.index(&ty.namespace), Extends: TypeDefOrRef::TypeRef(enum_type).encode(), FieldList: tables.Field.len() as _, - MethodList: 0, + MethodList: tables.MethodDef.len() as _, }); let enum_type = Type::named(&ty.namespace, &ty.name); let flags = FieldAttributes::PRIVATE | FieldAttributes::SPECIAL | FieldAttributes::RUNTIME_SPECIAL; @@ -145,11 +152,32 @@ pub fn write(name: &str, winrt: bool, definitions: &[Item], assemblies: &[&str]) } } Item::Interface(ty) => { - let mut flags = TypeAttributes::PUBLIC | TypeAttributes::INTERFACE; + let mut flags = TypeAttributes::PUBLIC | TypeAttributes::INTERFACE | TypeAttributes::ABSTRACT; if winrt { flags |= TypeAttributes::WINRT; } - tables.TypeDef.push(tables::TypeDef { Flags: flags.0, TypeName: strings.index(&ty.name), TypeNamespace: strings.index(&ty.namespace), Extends: 0, FieldList: 0, MethodList: 0 }); + tables.TypeDef.push(tables::TypeDef { + Flags: flags.0, + TypeName: strings.index(&ty.name), + TypeNamespace: strings.index(&ty.namespace), + Extends: 0, + FieldList: tables.Field.len() as _, + MethodList: tables.MethodDef.len() as _, + }); + for method in &ty.methods { + let flags = MethodAttributes::ABSTRACT | MethodAttributes::HIDE_BY_SIG | MethodAttributes::NEW_SLOT | MethodAttributes::PUBLIC | MethodAttributes::VIRTUAL; + tables.MethodDef.push(tables::MethodDef { + RVA: 0, + ImplFlags: 0, + Flags: flags.0, + Name: strings.index(&method.name), + Signature: blobs.index(&method_blob(method, definitions, references)), + ParamList: tables.Param.len() as _, + }); + for (sequence, param) in method.params.iter().enumerate() { + tables.Param.push(tables::Param { Flags: param_flags_to_attributes(param.flags).0, Sequence: (sequence + 1) as _, Name: strings.index(¶m.name) }); + } + } } } } @@ -174,6 +202,20 @@ fn type_reference<'a>(ty: &'a Type, definitions: &StagedDefinitions, assemblies: } } +fn param_flags_to_attributes(flags: ParamFlags) -> ParamAttributes { + let mut attributes = ParamAttributes(0); + if flags.contains(ParamFlags::INPUT) { + attributes |= ParamAttributes::INPUT; + } + if flags.contains(ParamFlags::OUTPUT) { + attributes |= ParamAttributes::OUTPUT; + } + if flags.contains(ParamFlags::OPTIONAL) { + attributes |= ParamAttributes::OPTIONAL; + } + attributes +} + fn item_type_name(item: &Item) -> (&str, &str) { match item { Item::Struct(ty) => (ty.namespace.as_str(), ty.name.as_str()), @@ -189,6 +231,16 @@ fn item_value_type(item: &Item) -> bool { } } +fn method_blob(method: &Method, definitions: &StagedDefinitions, references: &StagedReferences) -> Vec { + let mut blob = vec![0x20]; // HASTHIS + u32_blob(method.params.len() as _, &mut blob); + for param in &method.params { + type_blob(¶m.ty, &mut blob, definitions, references); + } + type_blob(&method.return_type, &mut blob, definitions, references); + blob +} + fn field_blob(ty: &Type, definitions: &StagedDefinitions, references: &StagedReferences) -> Vec { let mut blob = vec![0x6]; type_blob(ty, &mut blob, definitions, references); diff --git a/crates/libs/metadata/src/writer/imp/tables.rs b/crates/libs/metadata/src/writer/imp/tables.rs index ef5c86deae..7b34507389 100644 --- a/crates/libs/metadata/src/writer/imp/tables.rs +++ b/crates/libs/metadata/src/writer/imp/tables.rs @@ -248,11 +248,20 @@ impl Tables { buffer.write_u32(x.Signature); } - // for x in self.MethodDef { - - // } + for x in self.MethodDef { + buffer.write_u32(x.RVA); + buffer.write_u16(x.ImplFlags); + buffer.write_u16(x.Flags); + buffer.write_u32(x.Name); + buffer.write_u32(x.Signature); + buffer.write_index(x.ParamList, self.Param.len()); + } - // for x in self.Param {} + for x in self.Param { + buffer.write_u16(x.Flags); + buffer.write_u16(x.Sequence); + buffer.write_u32(x.Name); + } for x in self.Constant { buffer.write_u16(x.Type); diff --git a/crates/libs/metadata/src/writer/mod.rs b/crates/libs/metadata/src/writer/mod.rs index 4b1bbfd7a5..d64447bab8 100644 --- a/crates/libs/metadata/src/writer/mod.rs +++ b/crates/libs/metadata/src/writer/mod.rs @@ -39,6 +39,13 @@ impl Enum { pub struct Interface { pub namespace: String, pub name: String, + pub methods: Vec, +} + +impl Interface { + pub fn item(namespace: &str, name: &str, methods: Vec) -> Item { + Item::Interface(Self { namespace: namespace.to_string(), name: name.to_string(), methods }) + } } pub struct Field { @@ -63,6 +70,37 @@ impl Constant { } } +pub struct Method { + pub name: String, + pub return_type: Type, + pub params: Vec, +} + +impl Method { + pub fn new(name: &str, return_type: Type, params: Vec) -> Self { + Self { name: name.to_string(), return_type, params } + } +} + +pub struct Param { + pub name: String, + pub ty: Type, + pub flags: ParamFlags, +} + +impl Param { + pub fn new(name: &str, ty: Type, flags: ParamFlags) -> Self { + Self { name: name.to_string(), ty, flags } + } +} + +flags!(ParamFlags, u32); +impl ParamFlags { + pub const INPUT: Self = Self(0x1); + pub const OUTPUT: Self = Self(0x2); + pub const OPTIONAL: Self = Self(0x10); +} + pub enum Type { Void, Bool, diff --git a/crates/tests/metadata/tests/writer.rs b/crates/tests/metadata/tests/writer.rs index b53724e744..8a17b7fbeb 100644 --- a/crates/tests/metadata/tests/writer.rs +++ b/crates/tests/metadata/tests/writer.rs @@ -7,11 +7,14 @@ fn writer() { let mut items = vec![]; - items.push(Struct::item("test_metadata", "Inner", vec![Field::new("Value32", Type::F32), Field::new("Value64", Type::F64)])); + items.push(Struct::item("test_metadata", "A", vec![Field::new("A1", Type::F32), Field::new("A2", Type::F64)])); + items.push(Struct::item("test_metadata", "B", vec![Field::new("B1", Type::F32), Field::new("B2", Type::named("test_metadata", "A"))])); - items.push(Struct::item("test_metadata", "Outer", vec![Field::new("Value", Type::named("test_metadata", "Inner"))])); + items.push(Enum::item("test_metadata", "C", vec![Constant::new("C1", Value::I32(1)), Constant::new("C2", Value::I32(2))])); + items.push(Enum::item("test_metadata", "D", vec![Constant::new("D1", Value::I32(3)), Constant::new("D2", Value::I32(4))])); - items.push(Enum::item("test_metadata", "Flags", vec![Constant::new("One", Value::I32(1)), Constant::new("Two", Value::I32(2))])); + items.push(Interface::item("test_metadata", "E", vec![Method::new("E1", Type::Void, vec![]), Method::new("E2", Type::I32, vec![Param::new("p1", Type::I32, ParamFlags::INPUT), Param::new("p2", Type::F32, ParamFlags::OUTPUT), Param::new("p3", Type::F32, ParamFlags::INPUT | ParamFlags::OUTPUT)])])); + items.push(Interface::item("test_metadata", "F", vec![Method::new("F1", Type::Void, vec![]), Method::new("F2", Type::Void, vec![])])); let buffer = write("test_metadata", true, &items, &[]); std::fs::write(temp_file, buffer).unwrap(); diff --git a/crates/tests/riddle/Cargo.toml b/crates/tests/riddle/Cargo.toml new file mode 100644 index 0000000000..f2bf9ccca6 --- /dev/null +++ b/crates/tests/riddle/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "test_riddle" +version = "0.0.0" +authors = ["Microsoft"] +edition = "2018" + +[dependencies.windows-metadata] +path = "../../libs/metadata" diff --git a/crates/tests/riddle/src/basic.rs b/crates/tests/riddle/src/basic.rs new file mode 100644 index 0000000000..12b97b355a --- /dev/null +++ b/crates/tests/riddle/src/basic.rs @@ -0,0 +1,10 @@ +mod Root { + interface IRoot { + fn RootMethod(&self) -> f32; + } + mod Nested { + interface INested { + fn NestedMethod(&self) -> f32; + } + } +} diff --git a/crates/tests/riddle/src/lib.rs b/crates/tests/riddle/src/lib.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/crates/tests/riddle/src/lib.rs @@ -0,0 +1 @@ + diff --git a/crates/tests/riddle/tests/basic.rs b/crates/tests/riddle/tests/basic.rs new file mode 100644 index 0000000000..80818e998d --- /dev/null +++ b/crates/tests/riddle/tests/basic.rs @@ -0,0 +1,46 @@ +use std::process::Command; +use windows_metadata::reader::*; + +#[test] +fn basic() { + // For visual inspection: ildasm.exe %temp%\test_riddle.winmd + let output = std::env::temp_dir().join("test_riddle.winmd").to_string_lossy().into_owned(); + + let mut command = Command::new("cargo.exe"); + command.arg("install").arg("--path").arg("../../tools/riddle"); + + if !command.status().unwrap().success() { + panic!("Failed to install riddle"); + } + + let mut command = Command::new("riddle.exe"); + command.arg("-input").arg("src/basic.rs").arg("-output").arg(&output); + + if !command.status().unwrap().success() { + panic!("Failed to run riddle"); + } + + let files = File::with_default(&[&output]).expect("Failed to open winmd files"); + let reader = &Reader::new(&files); + + let root = reader.tree("Root", &[]).expect("Root namespace not found"); + assert_eq!(root.namespace, "Root"); + assert_eq!(root.nested.len(), 1); + let nested = &root.nested["Nested"]; + assert_eq!(nested.namespace, "Root.Nested"); + assert_eq!(nested.nested.len(), 0); + + let types: Vec = reader.namespace_types("Root").collect(); + assert_eq!(types.len(), 1); + + assert_eq!(reader.type_def_namespace(types[0]), "Root"); + assert_eq!(reader.type_def_name(types[0]), "IRoot"); + assert_eq!(reader.type_def_kind(types[0]), TypeKind::Interface); + + let types: Vec = reader.namespace_types("Root.Nested").collect(); + assert_eq!(types.len(), 1); + + assert_eq!(reader.type_def_namespace(types[0]), "Root.Nested"); + assert_eq!(reader.type_def_name(types[0]), "INested"); + assert_eq!(reader.type_def_kind(types[0]), TypeKind::Interface); +} diff --git a/crates/tools/riddle/Cargo.toml b/crates/tools/riddle/Cargo.toml new file mode 100644 index 0000000000..432464a202 --- /dev/null +++ b/crates/tools/riddle/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "riddle" +version = "0.0.1" +authors = ["Microsoft"] +edition = "2018" +license = "MIT OR Apache-2.0" +description = "Code gen support for the windows crate" +repository = "https://github.com/microsoft/windows-rs" + +[dependencies] +metadata = { package = "windows-metadata", path = "../../libs/metadata", version = "0.44.0" } +syn = { version = "1.0", features = ["full", "extra-traits"] } +proc-macro2 = {version = "1.0", features = ["span-locations"] } diff --git a/crates/tools/riddle/src/main.rs b/crates/tools/riddle/src/main.rs new file mode 100644 index 0000000000..6189437ea8 --- /dev/null +++ b/crates/tools/riddle/src/main.rs @@ -0,0 +1,183 @@ +use metadata::writer; +use std::io::Read; +use syn::{parse::*, *}; + +mod keywords { + syn::custom_keyword!(interface); +} + +#[derive(Debug)] +struct Module { + pub name: Ident, + pub members: Vec, +} + +impl Parse for Module { + fn parse(input: ParseStream) -> Result { + input.parse::()?; + let name = input.parse::()?; + let content; + braced!(content in input); + let mut members = Vec::new(); + while !content.is_empty() { + members.push(content.parse::()?); + } + Ok(Self { name, members }) + } +} + +#[derive(Debug)] +enum ModuleMember { + Module(Module), + Interface(Interface), +} + +impl Parse for ModuleMember { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(Token![mod]) { + Ok(ModuleMember::Module(input.parse::()?)) + } else if lookahead.peek(keywords::interface) { + Ok(ModuleMember::Interface(input.parse::()?)) + } else { + Err(lookahead.error()) + } + } +} + +#[derive(Debug)] +struct Interface { + pub name: Ident, + pub methods: Vec, +} + +impl Parse for Interface { + fn parse(input: ParseStream) -> Result { + input.parse::()?; + let name = input.parse::()?; + let content; + braced!(content in input); + let mut methods = Vec::new(); + while !content.is_empty() { + methods.push(content.parse::()?); + } + Ok(Self { name, methods }) + } +} + +fn module_to_writer(namespace: &str, module: &Module, items: &mut Vec) -> Result<()> { + for member in &module.members { + match member { + ModuleMember::Module(module) => module_to_writer(&format!("{namespace}.{}", module.name), module, items)?, + ModuleMember::Interface(interface) => interface_to_writer(namespace, interface, items)?, + } + } + Ok(()) +} + +fn interface_to_writer(namespace: &str, interface: &Interface, items: &mut Vec) -> Result<()> { + let mut methods = Vec::new(); + + for method in &interface.methods { + methods.push(writer::Method { name: method.sig.ident.to_string(), return_type: writer::Type::Void, params: vec![] }); + } + + items.push(writer::Interface::item(namespace, &interface.name.to_string(), methods)); + Ok(()) +} + +fn main() { + if let Err(message) = run() { + eprintln!("error: {message}"); + std::process::exit(1); + } +} + +type ToolResult = std::result::Result<(), String>; + +fn run() -> ToolResult { + let mut kind = ArgKind::None; + let mut merge = Vec::::new(); + let mut input = Vec::::new(); + let mut reference = Vec::::new(); + let mut output = String::new(); + let mut winrt = true; + + for arg in std::env::args().skip(1) { + if arg.starts_with('-') { + kind = ArgKind::None; + } + match kind { + ArgKind::None => match arg.as_str() { + "-merge" => kind = ArgKind::Merge, + "-input" => kind = ArgKind::Input, + "-ref" => kind = ArgKind::Reference, + "-output" => kind = ArgKind::Output, + "-win32" => { + winrt = false; + kind = ArgKind::None; + } + _ => print_help()?, + }, + ArgKind::Merge => merge.push(arg), + ArgKind::Input => input.push(arg), + ArgKind::Reference => reference.push(arg), + ArgKind::Output => { + if output.is_empty() { + output = arg; + } else { + print_help()?; + } + } + } + } + + if merge.len() + input.len() == 0 || output.is_empty() { + print_help()?; + } + + let mut items = Vec::new(); + + // for merge in merge { + // // TODO: write the types in the winmd into `items` + // } + + for filename in &input { + let mut file = std::fs::File::open(filename).map_err(|_| format!("failed to open `{filename}`"))?; + let mut source = String::new(); + file.read_to_string(&mut source).map_err(|_| format!("failed to read `{filename}`"))?; + + if let Err(error) = parse_str::(&source).and_then(|module| module_to_writer(&module.name.to_string(), &module, &mut items)) { + let start = error.span().start(); + return Err(format!("{error}\n --> {}:{:?}:{:?} ", filename, start.line, start.column)); + } + } + + let buffer = writer::write("test", winrt, &items, &[]); + std::fs::write(&output, buffer).map_err(|_| format!("failed to write `{output}`")) +} + +fn print_help() -> ToolResult { + Err(r#"riddle.exe [options...] + +options: + -input Path to source file with type definitions to parse + -merge Path to file or directory containing .winmd files to merge + -ref Path to file or directory containing .winmd files to reference + -output Path to .winmd file to generate + -win32 Kind of .winmd to generate; default is WinRT + +examples: + riddle -input first.rs second.rs -output out.winmd -ref local + riddle -merge first.winmd second.winmd -output out.winmd +"# + .to_string()) +} + +enum ArgKind { + None, + Merge, + Input, + Reference, + Output, +}