gir.rs 7.43 KB
Newer Older
1
2
use std::fs::File;

3
use quote::ToTokens;
4
use xml::writer::{EmitterConfig, XmlEvent};
5
use xml::EventWriter;
6

7
use hir::{Program, Class, Slot, Method, Ty, FnArg};
8
9
10
use glib_utils::*;
use gen::names::Names;

11
use xml::writer::Error as EmitterError;
12

13
14
15
16
17
18
19
20
error_chain! {
    foreign_links {
        Io(::std::io::Error) #[cfg(unix)];
        GIR(EmitterError);
    }
}

pub fn generate(program: &Program) -> Result<()> {
21
    for class in program.classes.iter() {
22
        generate_gir(class)?;
23
    }
24
25

    Ok(())
26
27
}

28
pub fn generate_gir(class: &Class) -> Result<()> {
29
    if let Some(ref s) = class.gir {
30
        let mut f = File::create(&s.value())?;
31
32
33
34
35
36
37
38
39

        let mut w = EmitterConfig::new()
            .perform_indent(true)
            .create_writer(&mut f);

        let name = class.name.to_string();
        let lower_name = lower_case_instance_name(&name).to_string();
        let names = Names::new(&class.name, "Class");

40
41
42
43
44
45
46
47
48
49
50
        // TODO: Get the version and shared-library from Cargo.toml?
        let version = "1.0.0";
        let slib = format!("lib{}.so", lower_name);

        // TODO: Find the parent namespace and class
        let parent_name = "GObject.Object";

        // TODO: include deps like GLib, etc
        // the dep should be `("GLib", "2.0")` lib and version
        let deps: Vec<(&str, &str)> = vec![];

51
52
53
54
        w.write(XmlEvent::start_element("repository")
            .attr("version", "1.2")
            .attr("xmlns", "http://www.gtk.org/introspection/core/1.0")
            .attr("xmlns:c", "http://www.gtk.org/introspection/c/1.0")
55
            .attr("xmlns:glib", "http://www.gtk.org/introspection/glib/1.0"))?;
56

57
        // <include name="GLib" version="2.0"/>
58
59
60
61
62
63
64
        for (name, version) in deps {
            w.write(XmlEvent::start_element("include")
                .attr("name", name)
                .attr("version", version)
            )?;
            w.write(XmlEvent::end_element())?;
        }
65
66
67
68
69

        w.write(XmlEvent::start_element("namespace")
            .attr("name", &name)
            .attr("c:identifier-prefixes", &name)
            .attr("c:symbol-prefixes", &lower_name)
70
71
            .attr("version", &version)
            .attr("shared-library", &slib)
72
        )?;
73
74
75
76
77
78
79
80

        w.write(XmlEvent::start_element("class")
            .attr("name", &name)
            .attr("c:type", &name)
            .attr("glib:type-name", &name)
            .attr("c:symbol-prefix", &lower_name)
            .attr("glib:type-struct", &names.vtable().to_string())
            .attr("glib:get-type", &names.get_type_fn().to_string())
81
            .attr("parent", &parent_name)
82
        )?;
83

84
85
        gen_constructor_xml(&mut w, &names)?;

86
        for slot in class.slots.iter() {
87
            gen_slot_xml(&mut w, &names, slot)?;
88
        }
89
90

        // closing class
91
        w.write(XmlEvent::end_element())?;
92

93
        gen_record_xml(&mut w, &names)?;
94

95
        // closing namespace
96
        w.write(XmlEvent::end_element())?;
97
        // closing repository
98
        w.write(XmlEvent::end_element())?;
99
    }
100
101

    Ok(())
102
103
}

104
fn gen_slot_xml(w: &mut EventWriter<&mut File>, names: &Names, slot: &Slot) -> Result<()> {
105
    // <method name="add" c:identifier="counter_add">
106
107
108
109
    //   <return-value transfer-ownership="none">
    //     <type name="gint" c:type="gint"/>
    //   </return-value>
    //   <parameters>
110
111
112
    //     <parameter name="x" transfer-ownership="none">
    //       <type name="gint" c:type="gint"/>
    //     </parameter>
113
114
    //   </parameters>
    // </method>
115
116
117
118
119
    // <method name="get" c:identifier="counter_get">
    //   <return-value transfer-ownership="none">
    //     <type name="gint" c:type="gint"/>
    //   </return-value>
    // </method>
120
121

    match slot {
122
123
124
125
126
127
128
129
        Slot::Method(Method {
            public: true,
            ref sig,
            ..
        }) => {
            let name = sig.name.to_string();
            let identifier = names.exported_fn(&sig.name).to_string();
            let output_ctype = type_to_ctype(&sig.output);
130
131
132
133
134
            w.write(XmlEvent::start_element("method")
                .attr("name", &name)
                .attr("c:identifier", &identifier)
            )?;

135
136
137
138
139
140
141
142
            // FIXME, calculate the ownership transfer for pointer types
            w.write(XmlEvent::start_element("return-value").attr("transfer-ownership", "none"))?;
            w.write(XmlEvent::start_element("type").attr("name", &output_ctype)
                                                   .attr("c:type", &output_ctype))?;
            w.write(XmlEvent::end_element())?; // closing type
            w.write(XmlEvent::end_element())?; // closing return-value

            // TODO: add parameters here
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
            w.write(XmlEvent::start_element("parameters"))?;
            for param in sig.inputs.iter() {
                if let FnArg::Arg { ref name, ref ty, .. } = param {
                    let ownership = "none";
                    let ctype = type_to_ctype(ty);

                    w.write(XmlEvent::start_element("parameter")
                        .attr("name", &name.to_string())
                        .attr("transfer-ownership", ownership))?;

                    w.write(XmlEvent::start_element("type")
                        .attr("name", &ctype).attr("c:type", &ctype))?;
                    w.write(XmlEvent::end_element())?; // closing type

                    w.write(XmlEvent::end_element())?; // closing parameter
                }
            }
            w.write(XmlEvent::end_element())?; // closing parameters

162
            // TODO: add doc here?
163
164
165
166
167
168
169
170
171
172
173

            w.write(XmlEvent::end_element())?;
        },
        _ => {
            // VirtualMethod or Signal
        }
    };

    Ok(())
}

174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
fn gen_constructor_xml(w: &mut EventWriter<&mut File>, names: &Names) -> Result<()> {
    // <constructor name="new" c:identifier="counter_new">
    //   <return-value transfer-ownership="full">
    //     <type name="Counter" c:type="Counter*"/>
    //   </return-value>
    // </constructor>

    let identifier = names.exported_fn_from_str("new").to_string();
    let name = names.instance().to_string();
    let ctype = format!("{}*", name);
    w.write(XmlEvent::start_element("constructor")
        .attr("name", "new")
        .attr("c:identifier", &identifier)
    )?;

    w.write(XmlEvent::start_element("return-value").attr("transfer-ownership", "full"))?;
    w.write(XmlEvent::start_element("type").attr("name", &name).attr("c:type", &ctype))?;
    w.write(XmlEvent::end_element())?; // closing type
    w.write(XmlEvent::end_element())?; // closing return-value

    // closing constructor
    w.write(XmlEvent::end_element())?;

    Ok(())
}

fn type_to_ctype(type_: &Ty) -> String {
    match *type_ {
        Ty::Integer(_) => "gint".to_string(),
        Ty::Unit => "void".to_string(),
        Ty::Char(_) => "gchar*".to_string(),
        Ty::Bool(_) => "gboolean".to_string(),
206
        // FIXME: These two need to be correctly implemented when support
207
208
209
210
211
        Ty::Borrowed(ref t) => format!("{}*", &type_to_ctype(t.as_ref())),
        Ty::Owned(ref t) => t.into_token_stream().to_string(),
    }
}

212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
fn gen_record_xml(w: &mut EventWriter<&mut File>, names: &Names) -> Result<()> {
    // <record name="CounterClass"
    //         c:type="CounterClass"
    //         disguised="1"
    //         glib:is-gtype-struct-for="Counter">
    // </record>
    w.write(XmlEvent::start_element("record")
        .attr("name", &names.vtable().to_string())
        .attr("disguised", "1")
        .attr("glib:is-gtype-struct-for", &names.instance().to_string())
    )?;
    w.write(XmlEvent::end_element())?;

    Ok(())
}