mod.rs 33.2 KB
Newer Older
1 2 3 4 5 6 7 8
// High-level Internal Representation of GObject artifacts
//
// Here we provide a view of the world in terms of what GObject knows:
// classes, interfaces, signals, etc.
//
// We construct this view of the world from the raw Abstract Syntax
// Tree (AST) from the previous stage.

9 10
use std::collections::HashMap;

11 12
use proc_macro2::{Ident, Span, TokenStream};
use quote::ToTokens;
13
use syn::punctuated::Punctuated;
14
use syn::spanned::Spanned;
15
use syn::{self, parse_str, Block, Field, LitStr, Path, ReturnType, Type};
16

17
use super::ast;
18 19
use super::checking::*;
use super::errors::*;
20
use super::glib_utils::*;
21
use super::ident_ext::IdentExt;
22 23

pub struct Program<'ast> {
24
    pub classes: Classes<'ast>,
25
    pub interfaces: Interfaces<'ast>,
26 27 28 29
}

pub struct Classes<'ast> {
    items: HashMap<Ident, Class<'ast>>,
30
}
31 32 33 34 35

pub struct Interfaces<'ast> {
    items: HashMap<Ident, Interface<'ast>>,
}

Jordan Petridis's avatar
Jordan Petridis committed
36
#[cfg_attr(rustfmt, rustfmt_skip)]
37
pub struct Class<'ast> {
38
    pub gir: Option<LitStr>,
39
    pub name: Ident, // Foo
40
    pub gobject_parent: bool,
41 42 43
    pub parent: TokenStream,           // Parent
    pub parent_ffi: TokenStream,       // ffi::Parent
    pub parent_class_ffi: TokenStream, // ffi::ParentClass
44
    pub implements: Vec<Path>,    // names of GTypeInterfaces
45 46

    // pub class_private: Option<&'ast ast::PrivateStruct>
47

48
    pub private_fields: Vec<&'ast Field>,
49

50
    // The order of these is important; it's the order of the slots in FooClass
51
    pub slots: Vec<Slot<'ast>>,
52
    // pub n_reserved_slots: usize,
53 54

    pub properties: Vec<Property<'ast>>,
55
    pub overrides: HashMap<Ident, Vec<Method<'ast>>>,
56 57
}

58 59 60 61 62
pub struct Interface<'ast> {
    pub name: Ident, // Foo

    // The order of these is important; it's the order of the slots in FooIface
    pub slots: Vec<Slot<'ast>>,
63 64
    // pub n_reserved_slots: usize,
    pub properties: Vec<Property<'ast>>,
65 66
}

67 68 69
pub enum Slot<'ast> {
    Method(Method<'ast>),
    VirtualMethod(VirtualMethod<'ast>),
Jordan Petridis's avatar
Jordan Petridis committed
70
    Signal(Signal<'ast>),
71 72
}

73 74 75 76 77 78 79 80 81 82 83 84
pub struct Property<'ast> {
    pub name: Ident,
    pub type_: &'ast Type,
    pub getter: &'ast Block,
    pub setter: PropertySetterBlock<'ast>,
}

pub struct PropertySetterBlock<'ast> {
    pub param: Ident,
    pub body: &'ast Block,
}

85
pub struct Method<'ast> {
86
    pub public: bool,
Alex Crichton's avatar
Alex Crichton committed
87
    pub sig: FnSig<'ast>,
88
    pub body: &'ast Block,
89 90
}

91
pub struct VirtualMethod<'ast> {
Alex Crichton's avatar
Alex Crichton committed
92
    pub sig: FnSig<'ast>,
93
    pub body: Option<&'ast Block>,
94 95
}

96 97 98 99 100 101 102 103 104 105 106
/// Represents a slot signature (method or signal).
///
/// This is different from syn::FnDecl because GObject slots only support a subset
/// of the things that Rust does.  This is encoded in our `FnArg` type for arguments
/// and the `Ty` for the return type.
///
/// `FnSig` has a number of convenience methods that return an `impl
/// ToTokens`, for when you need to emit code for different aspects of
/// the `FnSig`:  the Glib type that corresponds to the function's
/// return value, the input arguments with Glib types, the input
/// arguments converted *from* Glib types, and so on.
Alex Crichton's avatar
Alex Crichton committed
107 108 109 110 111 112 113
pub struct FnSig<'ast> {
    pub name: Ident,
    pub inputs: Vec<FnArg<'ast>>,
    pub output: Ty<'ast>,
}

pub enum FnArg<'ast> {
114
    SelfRef(Token!(&), Token!(self)),
Alex Crichton's avatar
Alex Crichton committed
115
    Arg {
116
        mutbl: Option<Token![mut]>,
Alex Crichton's avatar
Alex Crichton committed
117 118
        name: Ident,
        ty: Ty<'ast>,
Jordan Petridis's avatar
Jordan Petridis committed
119
    },
Alex Crichton's avatar
Alex Crichton committed
120 121
}

122
pub struct Signal<'ast> {
123
    // FIXME: signal flags
124 125
    pub sig: FnSig<'ast>,
    pub body: Option<&'ast Block>,
126
}
127

Alex Crichton's avatar
Alex Crichton committed
128 129 130 131 132 133 134 135 136
pub enum Ty<'ast> {
    Unit,
    Char(Ident),
    Bool(Ident),
    Borrowed(Box<Ty<'ast>>),
    Integer(Ident),
    Owned(&'ast syn::Path),
}

137 138
fn path_from_string(s: &str) -> Path {
    parse_str::<Path>(s).unwrap()
139 140
}

141
impl<'ast> Program<'ast> {
142 143
    pub fn from_ast_program(ast: &'ast ast::Program) -> Result<Program<'ast>> {
        check_program(ast)?;
144 145 146

        let mut classes = Classes::new();
        for class in ast.classes() {
147
            classes.add(class)?;
148 149 150 151
        }
        for impl_ in ast.impls() {
            classes.add_impl(impl_)?;
        }
152

153 154 155 156 157 158 159 160 161
        let mut interfaces = Interfaces::new();
        for iface in ast.interfaces() {
            interfaces.add(iface)?;
        }

        Ok(Program {
            classes,
            interfaces,
        })
162
    }
163
}
164

165 166 167 168
impl<'ast> Classes<'ast> {
    fn new() -> Classes<'ast> {
        Classes {
            items: HashMap::new(),
169
        }
170
    }
171

172 173
    pub fn len(&self) -> usize {
        self.items.len()
174 175
    }

176 177 178 179
    pub fn get(&self, name: &str) -> &Class {
        self.items.iter().find(|c| c.1.name == name).unwrap().1
    }

180
    fn extract_gir_from_attribute(class: &'ast ast::Class) -> Result<Option<LitStr>> {
181 182 183 184 185
        let mut res = None;
        for attr in class.attrs.iter() {
            if let Some(syn::Meta::List(metalist)) = attr.interpret_meta() {
                if metalist.ident == "generate_gir" {
                    if metalist.nested.len() != 1 {
186 187 188 189
                        return Err(Error::new(
                            metalist.ident.span(),
                            "Only one generate_gir allowed",
                        ));
190
                    }
191 192 193 194 195 196
                    match metalist
                        .nested
                        .into_iter()
                        .next()
                        .expect("Length checked above")
                    {
197 198
                        syn::NestedMeta::Literal(syn::Lit::Str(litstr)) => {
                            if res.is_some() {
199 200 201 202
                                return Err(Error::new(
                                    metalist.ident.span(),
                                    "multiple generate_gir attributes found",
                                ));
203 204
                            }
                            res = Some(litstr);
205 206 207 208 209 210 211
                        }
                        _ => {
                            return Err(Error::new(
                                metalist.ident.span(),
                                "generate_gir expects one string literal",
                            ));
                        }
212 213 214 215 216 217 218
                    }
                }
            }
        }
        Ok(res)
    }

Jordan Petridis's avatar
Jordan Petridis committed
219
    fn add(&mut self, ast_class: &'ast ast::Class) -> Result<()> {
220
        let gir = Self::extract_gir_from_attribute(ast_class)?;
Jordan Petridis's avatar
Jordan Petridis committed
221
        let prev = self.items.insert(
222
            ast_class.name.clone(),
Jordan Petridis's avatar
Jordan Petridis committed
223
            Class {
224
                name: ast_class.name.clone(),
225
                gir,
Jordan Petridis's avatar
Jordan Petridis committed
226 227 228 229 230
                gobject_parent: ast_class.extends.is_none(),
                parent: tokens_ParentInstance(ast_class),
                parent_ffi: tokens_ParentInstanceFfi(ast_class),
                parent_class_ffi: tokens_ParentClassFfi(ast_class),
                implements: Vec::new(),
231
                private_fields: ast_class.fields.named.iter().collect(),
Jordan Petridis's avatar
Jordan Petridis committed
232
                slots: Vec::new(),
233
                properties: Vec::new(),
Jordan Petridis's avatar
Jordan Petridis committed
234 235 236
                overrides: HashMap::new(),
            },
        );
237
        if prev.is_some() {
238 239 240 241
            return Err(Error::new(
                ast_class.name.span(),
                format!("redefinition of class `{}`", ast_class.name),
            ));
242 243 244 245
        }
        Ok(())
    }

246
    fn add_impl(&mut self, impl_: &'ast ast::Impl) -> Result<()> {
247 248
        let class = match self.items.get_mut(&impl_.self_path) {
            Some(class) => class,
249 250 251 252
            None => Err(Error::new(
                impl_.self_path.span(),
                format!("impl for class that doesn't exist: {}", impl_.self_path),
            ))?,
253
        };
254
        match *impl_ {
255 256
            ast::Impl {
                is_interface: false,
257
                trait_: Some(ref parent_class),
258 259
                ..
            } => {
260 261 262
                for item in impl_.items.iter() {
                    let item = match item.node {
                        ast::ImplItemKind::Method(ref m) => m,
263
                        ast::ImplItemKind::ReserveSlots(ref l) => {
264 265 266 267
                            return Err(Error::new(
                                l.span(),
                                "can't reserve slots in a parent class impl",
                            ));
268
                        }
269 270 271 272 273
                        ast::ImplItemKind::Prop(ast::ImplProp { ref name, .. }) => {
                            return Err(Error::new(
                                name.span(),
                                "can't define props in a parent class impl",
                            ));
274
                        }
275
                    };
276
                    if let Some(spanned) = &item.signal {
277 278 279 280
                        return Err(Error::new(
                            spanned.span(),
                            "can't implement signals for parent classes",
                        ));
281
                    }
282
                    if !item.virtual_.is_some() {
283 284 285 286
                        return Err(Error::new(
                            item.name.span(),
                            "can only implement virtual functions for parent classes",
                        ));
287
                    }
288
                    if let Some(spanned) = &item.public {
289 290 291 292
                        return Err(Error::new(
                            spanned.span(),
                            "overrides are always public, no `pub` needed",
                        ));
293
                    }
294
                    let method = match Slot::translate_from_method(item)? {
Jordan Petridis's avatar
Jordan Petridis committed
295 296 297 298 299 300 301 302
                        Slot::VirtualMethod(VirtualMethod {
                            sig,
                            body: Some(body),
                        }) => Method {
                            public: false,
                            sig,
                            body,
                        },
303
                        Slot::VirtualMethod(VirtualMethod { .. }) => {
304 305 306 307
                            return Err(Error::new(
                                item.name.span(),
                                "overrides must provide a body for virtual methods",
                            ));
308 309 310
                        }
                        _ => unreachable!(),
                    };
Jordan Petridis's avatar
Jordan Petridis committed
311 312
                    class
                        .overrides
313
                        .entry(parent_class.clone())
314
                        .or_insert_with(Vec::new)
315 316 317
                        .push(method);
                }
            }
318

319 320 321 322 323
            ast::Impl {
                is_interface: false,
                trait_: None,
                ..
            } => {
324
                for item in impl_.items.iter() {
325 326
                    match item.node {
                        ast::ImplItemKind::Prop(_) => {
327
                            let property = Property::translate_from_impl_item(item)?;
328
                            class.properties.push(property);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
329
                        }
330
                        _ => {
331
                            let slot = Slot::translate_from_item(item)?;
332 333
                            class.slots.push(slot);
                        }
334
                    }
335
                }
336
            }
337

338 339
            ast::Impl {
                is_interface: true,
340
                trait_: Some(ref _parent_class),
341 342
                ..
            } => unimplemented!(),
343

344
            _ => unreachable!(),
345 346 347 348
        }

        Ok(())
    }
349 350 351 352

    pub fn iter<'a>(&'a self) -> impl Iterator<Item = &'a Class> + 'a {
        self.items.values()
    }
353 354
}

355 356
impl<'ast> Slot<'ast> {
    fn translate_from_item(item: &'ast ast::ImplItem) -> Result<Slot<'ast>> {
357 358
        assert_eq!(item.attrs.len(), 0); // attributes unimplemented
        match item.node {
359
            ast::ImplItemKind::Method(ref method) => Slot::translate_from_method(method),
360
            ast::ImplItemKind::ReserveSlots(ref _slots) => {
361
                panic!("reserve slots not implemented");
362
            }
363
            ast::ImplItemKind::Prop(_) => unreachable!(),
364 365 366
        }
    }

367
    fn translate_from_method(method: &'ast ast::ImplItemMethod) -> Result<Slot<'ast>> {
368
        if method.signal.is_some() {
369
            if let Some(spanned) = &method.public {
370 371 372 373 374 375 376
                return Err(Error::new(
                    spanned.span(),
                    format!(
                        "function `{}` is a signal so it doesn't need to be public",
                        method.name
                    ),
                ));
377 378
            }

379
            if let Some(spanned) = &method.virtual_ {
380 381 382 383 384 385 386
                return Err(Error::new(
                    spanned.span(),
                    format!(
                        "function `{}` is a signal so it doesn't need to be virtual",
                        method.name
                    ),
                ));
387 388
            }

389
            let sig = extract_sig(method)?;
390 391
            let body = match method.body {
                ast::ImplItemMethodBlock::Block(ref block) => Some(block),
392
                _ => None,
393
            };
394 395 396
            Ok(Slot::Signal(Signal {
                // FIXME: signal flags
                sig,
397
                body,
398
            }))
399
        } else if method.virtual_.is_some() {
400
            if let Some(spanned) = &method.public {
401 402 403 404 405 406 407
                return Err(Error::new(
                    spanned.span(),
                    format!(
                        "function `{}` is virtual so it doesn't need to be public",
                        method.name
                    ),
                ));
408
            }
409
            let sig = extract_sig(method)?;
410 411
            let body = match method.body {
                ast::ImplItemMethodBlock::Block(ref block) => Some(block),
412
                _ => None,
413
            };
414
            Ok(Slot::VirtualMethod(VirtualMethod { sig, body }))
415
        } else {
416
            let sig = extract_sig(method)?;
417
            Ok(Slot::Method(Method {
Alex Crichton's avatar
Alex Crichton committed
418
                sig,
419
                public: method.public.is_some(),
420 421 422 423 424 425
                body: method.body.as_ref().ok_or_else(|| {
                    Error::new(
                        method.name.span(),
                        format!("function `{}` requires a body", method.name),
                    )
                })?,
426
            }))
427 428
        }
    }
429
}
Alex Crichton's avatar
Alex Crichton committed
430

431 432 433 434
fn extract_sig<'ast>(method: &'ast ast::ImplItemMethod) -> Result<FnSig<'ast>> {
    Ok(FnSig {
        output: extract_output(&method.output)?,
        inputs: extract_inputs(&method.inputs)?,
435
        name: method.name.clone(),
436 437
    })
}
Alex Crichton's avatar
Alex Crichton committed
438

439 440 441 442
fn extract_output<'ast>(output: &'ast ReturnType) -> Result<Ty<'ast>> {
    match *output {
        ReturnType::Type(_, ref boxt) => Ty::extract_from_type(boxt),
        ReturnType::Default => Ok(Ty::Unit),
Alex Crichton's avatar
Alex Crichton committed
443
    }
444
}
Alex Crichton's avatar
Alex Crichton committed
445

Federico Mena Quintero's avatar
Federico Mena Quintero committed
446
fn extract_inputs<'ast>(punc: &'ast Punctuated<syn::FnArg, Token!(,)>) -> Result<Vec<FnArg<'ast>>> {
447 448 449 450 451 452 453 454 455
    punc.iter()
        .map(|arg| match *arg {
            syn::FnArg::Captured(syn::ArgCaptured {
                ref pat, ref ty, ..
            }) => {
                let (name, mutbl) = match *pat {
                    syn::Pat::Ident(syn::PatIdent {
                        by_ref: None,
                        mutability: m,
456
                        ref ident,
457 458
                        subpat: None,
                    }) => (ident, m),
459 460 461 462 463 464
                    _ => {
                        return Err(Error::new(
                            pat.span(),
                            "only bare identifiers are allowed as argument patterns",
                        ));
                    }
465 466 467 468
                };

                Ok(FnArg::Arg {
                    mutbl,
469
                    name: name.clone(),
470 471 472 473 474 475 476 477 478 479 480 481
                    ty: Ty::extract_from_type(ty)?,
                })
            }
            syn::FnArg::SelfRef(syn::ArgSelfRef {
                and_token,
                lifetime: None,
                mutability: None,
                self_token,
            }) => Ok(FnArg::SelfRef(and_token, self_token)),
            syn::FnArg::SelfRef(syn::ArgSelfRef {
                mutability: Some(..),
                ..
482 483 484
            }) => {
                return Err(Error::new(arg.span(), "&mut self not implemented yet"));
            }
485 486
            syn::FnArg::SelfRef(syn::ArgSelfRef {
                lifetime: Some(..), ..
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
            }) => {
                return Err(Error::new(
                    arg.span(),
                    "lifetime arguments on self not implemented yet",
                ));
            }
            syn::FnArg::SelfValue(_) => {
                return Err(Error::new(arg.span(), "by-value self not implemented"));
            }
            syn::FnArg::Inferred(_) => {
                return Err(Error::new(
                    arg.span(),
                    "cannot have inferred function arguments",
                ));
            }
            syn::FnArg::Ignored(_) => {
                return Err(Error::new(
                    arg.span(),
                    "cannot have ignored function arguments",
                ));
            }
        })
        .collect()
510 511 512
}

impl<'ast> Ty<'ast> {
513
    pub fn extract_from_type(t: &'ast syn::Type) -> Result<Ty<'ast>> {
Alex Crichton's avatar
Alex Crichton committed
514
        match *t {
515 516 517 518 519 520 521 522 523
            syn::Type::Slice(_) => {
                return Err(Error::new(t.span(), "slice types not implemented yet"));
            }
            syn::Type::Array(_) => {
                return Err(Error::new(t.span(), "array types not implemented yet"));
            }
            syn::Type::Ptr(_) => {
                return Err(Error::new(t.span(), "ptr types not implemented yet"));
            }
Jordan Petridis's avatar
Jordan Petridis committed
524 525
            syn::Type::Reference(syn::TypeReference {
                lifetime: Some(_), ..
526 527 528 529 530 531
            }) => {
                return Err(Error::new(
                    t.span(),
                    "borrowed types with lifetimes not implemented yet",
                ));
            }
Jordan Petridis's avatar
Jordan Petridis committed
532 533 534 535 536 537
            syn::Type::Reference(syn::TypeReference {
                lifetime: None,
                ref elem,
                ref mutability,
                ..
            }) => {
538
                if mutability.is_some() {
539 540 541 542
                    return Err(Error::new(
                        t.span(),
                        "mutable borrowed pointers not implemented",
                    ));
Alex Crichton's avatar
Alex Crichton committed
543
                }
544
                let path = match **elem {
Jordan Petridis's avatar
Jordan Petridis committed
545 546 547 548
                    syn::Type::Path(syn::TypePath {
                        qself: None,
                        ref path,
                    }) => path,
549 550 551 552 553 554
                    _ => {
                        return Err(Error::new(
                            t.span(),
                            "only borrowed pointers to paths supported",
                        ));
                    }
Alex Crichton's avatar
Alex Crichton committed
555
                };
556
                let ty = Ty::extract_from_path(path)?;
Alex Crichton's avatar
Alex Crichton committed
557 558
                Ok(Ty::Borrowed(Box::new(ty)))
            }
559 560 561 562
            syn::Type::BareFn(_) => Err(Error::new(
                t.span(),
                "function pointer types not implemented yet",
            )),
563
            syn::Type::Never(_) => Err(Error::new(t.span(), "never not implemented yet")),
564
            syn::Type::Tuple(syn::TypeTuple { ref elems, .. }) => {
565
                if elems.is_empty() {
Alex Crichton's avatar
Alex Crichton committed
566 567
                    Ok(Ty::Unit)
                } else {
568
                    Err(Error::new(t.span(), "tuple types not implemented yet"))
Alex Crichton's avatar
Alex Crichton committed
569 570
                }
            }
571 572 573 574
            syn::Type::Path(syn::TypePath { qself: Some(_), .. }) => Err(Error::new(
                t.span(),
                "path types with qualified self (`as` syntax) not allowed",
            )),
Jordan Petridis's avatar
Jordan Petridis committed
575 576 577
            syn::Type::Path(syn::TypePath {
                qself: None,
                ref path,
578
            }) => Ty::extract_from_path(path),
579 580 581 582 583 584
            syn::Type::TraitObject(_) => {
                Err(Error::new(t.span(), "trait objects not implemented yet"))
            }
            syn::Type::ImplTrait(_) => {
                Err(Error::new(t.span(), "trait objects not implemented yet"))
            }
585 586
            syn::Type::Paren(syn::TypeParen { ref elem, .. }) => Ty::extract_from_type(elem),
            syn::Type::Group(syn::TypeGroup { ref elem, .. }) => Ty::extract_from_type(elem),
587 588 589
            syn::Type::Infer(_) => Err(Error::new(t.span(), "underscore types not allowed")),
            syn::Type::Macro(_) => Err(Error::new(t.span(), "type macros not allowed")),
            syn::Type::Verbatim(_) => Err(Error::new(t.span(), "type macros not allowed")),
Alex Crichton's avatar
Alex Crichton committed
590 591 592
        }
    }

593
    fn extract_from_path(t: &'ast syn::Path) -> Result<Ty<'ast>> {
Jordan Petridis's avatar
Jordan Petridis committed
594 595 596
        if t.segments.iter().any(|segment| match segment.arguments {
            syn::PathArguments::None => false,
            _ => true,
Alex Crichton's avatar
Alex Crichton committed
597
        }) {
598 599 600 601
            return Err(Error::new(
                t.span(),
                "type or lifetime parameters not allowed",
            ));
Alex Crichton's avatar
Alex Crichton committed
602 603
        }
        if t.leading_colon.is_some() || t.segments.len() > 1 {
Jordan Petridis's avatar
Jordan Petridis committed
604
            return Ok(Ty::Owned(t));
Alex Crichton's avatar
Alex Crichton committed
605 606
        }

607
        let ident = &t.segments.first().unwrap().value().ident;
Alex Crichton's avatar
Alex Crichton committed
608

609
        match ident.to_owned_string().as_str() {
610 611
            "char" => Ok(Ty::Char(ident.clone())),
            "bool" => Ok(Ty::Bool(ident.clone())),
Jordan Petridis's avatar
Jordan Petridis committed
612
            "i8" | "i16" | "i32" | "i64" | "isize" | "u8" | "u16" | "u32" | "u64" | "usize" => {
613
                Ok(Ty::Integer(ident.clone()))
Alex Crichton's avatar
Alex Crichton committed
614 615 616 617
            }
            _other => Ok(Ty::Owned(t)),
        }
    }
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648

    pub fn to_gtype_string(&self) -> &'static str {
        match *self {
            Ty::Unit => "gobject_sys::G_TYPE_NONE",
            Ty::Char(_) => "gobject_sys::G_TYPE_UINT", // <char as ToGlib>::GlibType = u32
            Ty::Bool(_) => "gobject_sys::G_TYPE_BOOLEAN",
            Ty::Borrowed(_) => unimplemented!(),

            Ty::Integer(ref ident) => match ident.to_owned_string().as_str() {
                "i8" => "gobject_sys::G_TYPE_CHAR",
                "i16" => unimplemented!("should we promote i16 to i32?"),
                "i32" => "gobject_sys::G_TYPE_INT",
                "i64" => "gobject_sys::G_TYPE_INT64",
                "isize" => unimplemented!(),

                "u8" => "gobject_sys::G_TYPE_UCHAR",
                "u16" => unimplemented!("should we promote u16 to u32?"),
                "u32" => "gobject_sys::G_TYPE_UINT",
                "u64" => "gobject_sys::G_TYPE_UINT64",
                "usize" => unimplemented!(),

                _ => unreachable!(),
            },

            Ty::Owned(_) => unimplemented!(),
        }
    }

    pub fn to_gtype_path(&self) -> Path {
        path_from_string(self.to_gtype_string())
    }
649 650 651 652 653 654

    /// Returns tokens for the name of the `g_param_spec_*()` function that can be
    /// used to create a `GParamSpec` suitable for the `Ty`.
    pub fn to_gparam_spec_constructor(&self) -> TokenStream {
        match *self {
            Ty::Unit => unreachable!("there is no g_param_spec_*() for the Unit type"),
655 656
            Ty::Char(_) => quote! { gobject_ffi::g_param_spec_uint }, // <char as ToGlib>::
            // GlibType = u32
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
            Ty::Bool(_) => quote! { gobject_ffi::g_param_spec_boolean },
            Ty::Borrowed(_) => unimplemented!(),

            Ty::Integer(ref ident) => match ident.to_owned_string().as_str() {
                "i8" | "u8" => quote! { gobject_ffi::g_param_spec_char },
                "i16" => unimplemented!("should we promote i16 to i32?"),
                "i32" => quote! { gobject_ffi::g_param_spec_int },
                "i64" => quote! { gobject_ffi::g_param_spec_int64 },
                "isize" => unimplemented!(),

                "u16" => unimplemented!("should we promote u16 to u32?"),
                "u32" => quote! { gobject_ffi::g_param_spec_uint },
                "u64" => quote! { gobject_ffi::g_param_spec_uint64 },
                "usize" => unimplemented!(),

                _ => unreachable!(),
            },

            Ty::Owned(_) => unimplemented!(),
        }
    }

    /// Returns tokens for the minimum/maximum/default values that are passed to
    /// the `g_param_spec_*()` functions when constructing a `GParamSpec`.
    pub fn to_gparam_spec_min_max_default(&self) -> TokenStream {
        match *self {
            Ty::Unit => unreachable!("there is no g_param_spec_*() for the Unit type"),
Federico Mena Quintero's avatar
Federico Mena Quintero committed
684 685
            Ty::Char(_) => quote! { std::u32::MIN, std::u32::MAX, 0 }, /* <char as ToGlib>::
                                                                         * GlibType = u32 */
686 687 688 689 690 691 692 693 694 695 696 697 698 699

            // g_param_spec_bool() takes no min/max, just a default
            Ty::Bool(_) => quote! { glib_ffi::GFALSE },

            Ty::Borrowed(_) => unimplemented!(),

            Ty::Integer(ref ident) => match ident.to_owned_string().as_str() {
                "i8" | "u8" => quote! { 0, 0xff as i8, 0 },
                "i16" => unimplemented!("should we promote i16 to i32?"),
                "i32" => quote! { i32::MIN, i32::MAX, 0 },
                "i64" => quote! { i64::MIN, i64::MAX, 0 },
                "isize" => unimplemented!(),

                "u16" => unimplemented!("should we promote u16 to u32?"),
700
                "u32" => quote! { std::u32::MIN, std::u32::MAX, 0 },
701 702 703 704 705 706 707 708 709
                "u64" => quote! { u64::MIN, u64::MAX, 0 },
                "usize" => unimplemented!(),

                _ => unreachable!(),
            },

            Ty::Owned(_) => unimplemented!(),
        }
    }
710 711 712 713

    pub fn to_gvalue_setter(&self) -> TokenStream {
        match *self {
            Ty::Unit => unreachable!("there is no g_value_set_*() for the Unit type"),
714 715
            Ty::Char(_) => quote! { gobject_ffi::g_value_set_uint }, // <char as ToGlib>::
            // GlibType = u32
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
            Ty::Bool(_) => quote! { gobject_ffi::g_value_set_bool },
            Ty::Borrowed(_) => unimplemented!(),

            Ty::Integer(ref ident) => match ident.to_owned_string().as_str() {
                "i8" | "u8" => quote! { gobject_ffi::g_value_set_char },
                "i16" => unimplemented!("should we promote i16 to i32?"),
                "i32" => quote! { gobject_ffi::g_value_set_int },
                "i64" => quote! { gobject_ffi::g_value_set_int64 },
                "isize" => unimplemented!(),

                "u16" => unimplemented!("should we promote u16 to u32?"),
                "u32" => quote! { gobject_ffi::g_value_set_uint },
                "u64" => quote! { gobject_ffi::g_value_set_uint64 },
                "usize" => unimplemented!(),

                _ => unreachable!(),
            },

            Ty::Owned(_) => unimplemented!(),
        }
    }
737 738
}

739 740 741 742
impl<'ast> Property<'ast> {
    fn translate_from_impl_item(item: &'ast ast::ImplItem) -> Result<Property<'ast>> {
        assert_eq!(item.attrs.len(), 0); // attributes unimplemented
        if let ast::ImplItemKind::Prop(ref prop) = item.node {
743
            let name = &prop.name;
744 745 746 747
            let type_ = &prop.type_;

            let getter = match prop.getter() {
                Some(&ast::ImplPropBlock::Getter(ref b)) => b,
748 749 750 751 752 753 754 755 756 757 758 759
                None => {
                    return Err(Error::new(
                        prop.name.span(),
                        format!("property without getter: {}", name),
                    ));
                }
                _ => {
                    return Err(Error::new(
                        prop.name.span(),
                        format!("invalid property getter: {}", name),
                    ));
                }
760 761 762 763
            };

            let setter = match prop.setter() {
                Some(ast::ImplPropBlock::Setter(ref b)) => PropertySetterBlock {
764
                    param: b.param.clone(),
765 766
                    body: &b.block,
                },
767 768 769 770 771 772 773 774 775 776 777 778
                None => {
                    return Err(Error::new(
                        prop.name.span(),
                        format!("property without setter: {}", name),
                    ));
                }
                _ => {
                    return Err(Error::new(
                        prop.name.span(),
                        format!("invalid property setter: {}", name),
                    ));
                }
779 780 781
            };

            return Ok(Property {
782
                name: name.clone(),
783
                type_,
784
                getter: &getter.block,
785 786 787 788
                setter,
            });
        }

789 790 791 792
        return Err(Error::new(
            item.node.ident_span(),
            "Invalid definition inside property",
        ));
793 794 795
    }
}

796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
impl<'ast> Interfaces<'ast> {
    fn new() -> Interfaces<'ast> {
        Interfaces {
            items: HashMap::new(),
        }
    }

    pub fn len(&self) -> usize {
        self.items.len()
    }

    pub fn get(&self, name: &str) -> &Interface {
        self.items.iter().find(|c| c.1.name == name).unwrap().1
    }

    fn add(&mut self, ast_iface: &'ast ast::Interface) -> Result<()> {
812
        let slots = self.translate_slots(ast_iface)?;
813
        let properties = self.translate_properties(ast_iface)?;
814

815
        let prev = self.items.insert(
816
            ast_iface.name.clone(),
817
            Interface {
818
                name: ast_iface.name.clone(),
819
                slots,
820
                properties,
821 822
            },
        );
823
        if let Some(_) = prev {
824 825 826 827
            Err(Error::new(
                ast_iface.name.span(),
                format!("redefinition of interface `{}`", ast_iface.name),
            ))
828 829
        } else {
            Ok(())
830 831
        }
    }
832

833 834 835 836 837
    fn translate_slots(&self, ast_iface: &'ast ast::Interface) -> Result<Vec<Slot<'ast>>> {
        let mut slots = Vec::new();

        for item in ast_iface.items.iter() {
            match item.node {
838
                ast::ImplItemKind::Prop(_) => (),
839 840

                _ => {
841 842
                    let slot = Slot::translate_from_item(item)?;
                    slots.push(slot);
843 844 845 846 847 848 849
                }
            }
        }

        Ok(slots)
    }

850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866
    fn translate_properties(&self, ast_iface: &'ast ast::Interface) -> Result<Vec<Property<'ast>>> {
        let mut properties = Vec::new();

        for item in ast_iface.items.iter() {
            match item.node {
                ast::ImplItemKind::Prop(_) => {
                    let property = Property::translate_from_impl_item(item)?;
                    properties.push(property);
                }

                _ => (),
            }
        }

        Ok(properties)
    }

867 868 869
    pub fn iter<'a>(&'a self) -> impl Iterator<Item = &'a Interface> + 'a {
        self.items.values()
    }
870 871
}

872
fn make_path_glib_object() -> Path {
873
    path_from_string("glib::Object")
874 875
}

Alex Crichton's avatar
Alex Crichton committed
876
impl<'a> ToTokens for FnArg<'a> {
877
    fn to_tokens(&self, tokens: &mut TokenStream) {
Alex Crichton's avatar
Alex Crichton committed
878 879 880 881 882
        match *self {
            FnArg::SelfRef(and, self_) => {
                and.to_tokens(tokens);
                self_.to_tokens(tokens);
            }
Jordan Petridis's avatar
Jordan Petridis committed
883
            FnArg::Arg {
884
                ref name,
Jordan Petridis's avatar
Jordan Petridis committed
885
                ref ty,
886
                ref mutbl,
Jordan Petridis's avatar
Jordan Petridis committed
887
            } => {
Alex Crichton's avatar
Alex Crichton committed
888 889
                mutbl.to_tokens(tokens);
                name.to_tokens(tokens);
890
                Token!(:)([Span::call_site()]).to_tokens(tokens);
Alex Crichton's avatar
Alex Crichton committed
891 892 893 894 895 896 897
                ty.to_tokens(tokens);
            }
        }
    }
}

impl<'a> ToTokens for Ty<'a> {
898
    fn to_tokens(&self, tokens: &mut TokenStream) {
Alex Crichton's avatar
Alex Crichton committed
899
        match *self {
900
            Ty::Unit => tokens.extend(quote!{ () }),
901 902 903
            Ty::Char(ref tok) => tok.to_tokens(tokens),
            Ty::Bool(ref tok) => tok.to_tokens(tokens),
            Ty::Integer(ref t) => t.to_tokens(tokens),
Alex Crichton's avatar
Alex Crichton committed
904
            Ty::Borrowed(ref t) => {
905
                Token!(&)([Span::call_site()]).to_tokens(tokens);
Alex Crichton's avatar
Alex Crichton committed
906 907 908 909 910 911 912
                t.to_tokens(tokens)
            }
            Ty::Owned(t) => t.to_tokens(tokens),
        }
    }
}

913
pub mod tests {
914 915
    use super::*;

916 917 918
    pub fn run() -> Result<()> {
        creates_trivial_class()?;
        creates_class_with_superclass()?;
919
        maps_ty_to_gtype();
920
        Ok(())
921 922
    }

923 924
    fn test_class_and_superclass(raw: &str, class_name: &str, superclass_name: &str) -> Result<()> {
        let ast_program = parse_str::<ast::Program>(raw)?;
925

926
        let program = Program::from_ast_program(&ast_program)?;
927 928 929

        assert!(program.classes.len() == 1);

930
        let class = program.classes.get(class_name);
931
        assert_eq!(class.name.to_owned_string(), class_name);
932
        assert_eq!(class.parent.to_string(), superclass_name);
933
        Ok(())
934 935
    }

936
    fn creates_trivial_class() -> Result<()> {
937
        let raw = "class Foo {}";
938

939
        test_class_and_superclass(raw, "Foo", "glib :: Object")
940
    }
941

942
    fn creates_class_with_superclass() -> Result<()> {
943
        let raw = "class Foo: Bar {}";
944

945
        test_class_and_superclass(raw, "Foo", "Bar")
946
    }
947 948

    fn maps_ty_to_gtype() {
949
        assert_eq!(Ty::Unit.to_gtype_string(), "gobject_sys::G_TYPE_NONE");
950
        assert_eq!(
951
            Ty::Char(Ident::from_str("char")).to_gtype_string(),
952
            "gobject_sys::G_TYPE_UINT"
953 954
        );
        assert_eq!(
955
            Ty::Bool(Ident::from_str("bool")).to_gtype_string(),
956
            "gobject_sys::G_TYPE_BOOLEAN"
957 958
        );

959
        // assert_eq!(Ty::Borrowed(...).to_gtype_string(), ...);
960 961

        assert_eq!(
962
            Ty::Integer(Ident::from_str("i8")).to_gtype_string(),
963
            "gobject_sys::G_TYPE_CHAR"
964
        );
965
        // assert_eq!(Ty::Integer(Ident::from_str("i16")).to_gtype(), ...);
966
        assert_eq!(
967
            Ty::Integer(Ident::from_str("i32")).to_gtype_string(),
968
            "gobject_sys::G_TYPE_INT"
969 970
        );
        assert_eq!(
971
            Ty::Integer(Ident::from_str("i64")).to_gtype_string(),
972
            "gobject_sys::G_TYPE_INT64"
973
        );
974
        // assert_eq!(Ty::Integer(Ident::from_str("isize")).to_gtype_string(), ...);
975 976

        assert_eq!(
977
            Ty::Integer(Ident::from_str("u8")).to_gtype_string(),
978
            "gobject_sys::G_TYPE_UCHAR"
979
        );
980
        // assert_eq!(Ty::Integer(Ident::new("u16", Span::call_site())).to_gtype_string(), ...);
981
        assert_eq!(
982
            Ty::Integer(Ident::from_str("u32")).to_gtype_string(),
983
            "gobject_sys::G_TYPE_UINT"
984 985
        );
        assert_eq!(
986
            Ty::Integer(Ident::from_str("u64")).to_gtype_string(),
987
            "gobject_sys::G_TYPE_UINT64"
988
        );
989
        // assert_eq!(Ty::Integer(Ident::from_str("usize")).to_gtype_string(), ...);
990

991
        // assert_eq!(Ty::Owned(...).to_gtype_string(), ...);
992
    }
993
}