Parse discriminant values from clang AST
diff --git a/macro/src/clang.rs b/macro/src/clang.rs
index 953d0a5..a2e3425 100644
--- a/macro/src/clang.rs
+++ b/macro/src/clang.rs
@@ -1,13 +1,14 @@
 use crate::syntax::attrs::OtherAttrs;
 use crate::syntax::namespace::Namespace;
 use crate::syntax::report::Errors;
-use crate::syntax::{Api, Doc, Enum, ForeignName, Pair, Variant};
+use crate::syntax::{Api, Discriminant, Doc, Enum, ForeignName, Pair, Variant};
 use proc_macro2::Ident;
 use quote::format_ident;
 use serde::Deserialize;
 use std::env;
 use std::fs;
 use std::path::PathBuf;
+use std::str::FromStr;
 
 const CXX_CLANG_AST: &str = "CXX_CLANG_AST";
 
@@ -18,6 +19,8 @@
     NamespaceDecl(NamespaceDecl),
     EnumDecl(EnumDecl),
     EnumConstantDecl(EnumConstantDecl),
+    ImplicitCastExpr,
+    ConstantExpr(ConstantExpr),
     Unknown,
 }
 
@@ -36,6 +39,11 @@
     name: String,
 }
 
+#[derive(Deserialize)]
+struct ConstantExpr {
+    value: String,
+}
+
 pub fn load(cx: &mut Errors, apis: &mut [Api]) {
     let ref mut variants_from_header = Vec::new();
     for api in apis {
@@ -151,6 +159,32 @@
                     Ok(ident) => ident,
                     Err(_) => format_ident!("__Variant{}", enm.variants.len()),
                 };
+                let discriminant = match discriminant_value(&node.inner) {
+                    ParsedDiscriminant::Constant(discriminant) => discriminant,
+                    ParsedDiscriminant::Successor => match enm.variants.last() {
+                        None => Discriminant::zero(),
+                        Some(last) => match last.discriminant.checked_succ() {
+                            Some(discriminant) => discriminant,
+                            None => {
+                                let span = &enm.variants_from_header_attr;
+                                let msg = format!(
+                                    "overflow processing discriminant value for variant: {}",
+                                    decl.name,
+                                );
+                                return cx.error(span, msg);
+                            }
+                        },
+                    },
+                    ParsedDiscriminant::Fail => {
+                        let span = &enm.variants_from_header_attr;
+                        let msg = format!(
+                            "failed to obtain discriminant value for variant: {}",
+                            decl.name,
+                        );
+                        cx.error(span, msg);
+                        Discriminant::zero()
+                    }
+                };
                 enm.variants.push(Variant {
                     doc: Doc::new(),
                     attrs: OtherAttrs::none(),
@@ -159,7 +193,7 @@
                         cxx: cxx_name,
                         rust: rust_name,
                     },
-                    discriminant: unimplemented!(),
+                    discriminant,
                     expr: None,
                 });
             }
@@ -173,3 +207,33 @@
         let _ = namespace.pop().unwrap();
     }
 }
+
+enum ParsedDiscriminant {
+    Constant(Discriminant),
+    Successor,
+    Fail,
+}
+
+fn discriminant_value(mut clang: &[Node]) -> ParsedDiscriminant {
+    if clang.is_empty() {
+        // No discriminant expression provided; use successor of previous
+        // descriminant.
+        return ParsedDiscriminant::Successor;
+    }
+
+    loop {
+        if clang.len() != 1 {
+            return ParsedDiscriminant::Fail;
+        }
+
+        let node = &clang[0];
+        match &node.kind {
+            Clang::ImplicitCastExpr => clang = &node.inner,
+            Clang::ConstantExpr(expr) => match Discriminant::from_str(&expr.value) {
+                Ok(discriminant) => return ParsedDiscriminant::Constant(discriminant),
+                Err(_) => return ParsedDiscriminant::Fail,
+            },
+            _ => return ParsedDiscriminant::Fail,
+        }
+    }
+}
diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs
index 80f7c0b..01d0f1a 100644
--- a/syntax/discriminant.rs
+++ b/syntax/discriminant.rs
@@ -150,7 +150,7 @@
 }
 
 impl Discriminant {
-    const fn zero() -> Self {
+    pub const fn zero() -> Self {
         Discriminant {
             sign: Sign::Positive,
             magnitude: 0,
@@ -179,6 +179,28 @@
             magnitude: i.wrapping_abs() as u64,
         }
     }
+
+    pub const fn checked_succ(self) -> Option<Self> {
+        match self.sign {
+            Sign::Negative => {
+                if self.magnitude == 1 {
+                    Some(Discriminant::zero())
+                } else {
+                    Some(Discriminant {
+                        sign: Sign::Negative,
+                        magnitude: self.magnitude - 1,
+                    })
+                }
+            }
+            Sign::Positive => match self.magnitude.checked_add(1) {
+                Some(magnitude) => Some(Discriminant {
+                    sign: Sign::Positive,
+                    magnitude,
+                }),
+                None => None,
+            },
+        }
+    }
 }
 
 impl Display for Discriminant {
diff --git a/syntax/mod.rs b/syntax/mod.rs
index dd5de52..b22587f 100644
--- a/syntax/mod.rs
+++ b/syntax/mod.rs
@@ -30,7 +30,6 @@
 mod visit;
 
 use self::attrs::OtherAttrs;
-use self::discriminant::Discriminant;
 use self::namespace::Namespace;
 use self::parse::kw;
 use self::symbol::Symbol;
@@ -41,6 +40,7 @@
 
 pub use self::atom::Atom;
 pub use self::derive::{Derive, Trait};
+pub use self::discriminant::Discriminant;
 pub use self::doc::Doc;
 pub use self::names::ForeignName;
 pub use self::parse::parse_items;