Add ForeignName wrapper around non-Rust names
diff --git a/syntax/names.rs b/syntax/names.rs
index c4cbcff..b2ae013 100644
--- a/syntax/names.rs
+++ b/syntax/names.rs
@@ -1,25 +1,37 @@
+use crate::syntax::symbol::Segment;
 use crate::syntax::{Lifetimes, NamedType, Pair, Symbol};
 use proc_macro2::{Ident, Span};
+use std::fmt::{self, Display};
 use std::iter;
+use syn::parse::Result;
 use syn::punctuated::Punctuated;
 
+#[derive(Clone)]
+pub struct ForeignName {
+    text: String,
+    span: Span,
+}
+
 impl Pair {
     pub fn to_symbol(&self) -> Symbol {
-        Symbol::from_idents(self.iter_all_segments())
+        let segments = self
+            .namespace
+            .iter()
+            .map(|ident| ident as &dyn Segment)
+            .chain(iter::once(&self.cxx as &dyn Segment));
+        Symbol::from_idents(segments)
     }
 
     pub fn to_fully_qualified(&self) -> String {
         let mut fully_qualified = String::new();
-        for segment in self.iter_all_segments() {
+        for segment in &self.namespace {
             fully_qualified += "::";
             fully_qualified += &segment.to_string();
         }
+        fully_qualified += "::";
+        fully_qualified += &self.cxx.to_string();
         fully_qualified
     }
-
-    fn iter_all_segments(&self) -> impl Iterator<Item = &Ident> {
-        self.namespace.iter().chain(iter::once(&self.cxx))
-    }
 }
 
 impl NamedType {
@@ -36,3 +48,19 @@
         self.rust.span()
     }
 }
+
+impl ForeignName {
+    pub fn parse(text: &str, span: Span) -> Result<Self> {
+        // TODO: support C++ names containing whitespace (`unsigned int`) or
+        // non-alphanumeric characters (`operator++`).
+        let ident: Ident = syn::parse_str(text)?;
+        let text = ident.to_string();
+        Ok(ForeignName { text, span })
+    }
+}
+
+impl Display for ForeignName {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        formatter.write_str(&self.text)
+    }
+}