Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions const-oid/oiddbgen/src/asn1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use std::collections::{BTreeMap, HashSet};

use regex::Regex;

#[derive(Clone, Debug)]
pub struct Asn1Parser {
spec: String,
tree: BTreeMap<String, (Option<String>, Option<String>)>,
}

impl Asn1Parser {
const DEF: &'static str = r"(?mx)
(?P<name>[a-z][a-zA-Z0-9-]*) # name
\s+
OBJECT
\s+
IDENTIFIER
\s*
::=
\s*
\{
\s*
(?:(?P<base>[a-z][a-zA-Z0-9-]*)\s+)? # base
(?P<tail> # tail
(?:
(?:
[a-z][a-zA-Z0-9-]*\([0-9]+\)\s+
)
|
(?:
[0-9]+\s+
)
)*
)
\}
";

const ARC: &'static str = r"(?mx)
(?:
[a-z][a-zA-Z0-9-]*\(([0-9]+)\)
)
|
(?:
([0-9]+)
)
";

pub fn new(spec: String, asn1: &str) -> Self {
let def = Regex::new(Self::DEF).unwrap();
let arc = Regex::new(Self::ARC).unwrap();

let mut tree = BTreeMap::default();
for mat in def.find_iter(asn1) {
let caps = def.captures(mat.as_str()).unwrap();
let name = caps.name("name").unwrap().as_str().to_owned();
let base = caps.name("base").map(|m| m.as_str().to_string());
let tail = caps.name("tail").map(|m| {
arc.find_iter(m.as_str())
.map(|m| {
let c = arc.captures(m.as_str()).unwrap();
c.get(1).unwrap_or_else(|| c.get(2).unwrap()).as_str()
})
.collect::<Vec<_>>()
.join(".")
});

tree.insert(name, (base, tail));
}

Self { spec, tree }
}

pub fn resolve(&self, name: &str) -> Option<String> {
let (base, arcs) = self.tree.get(name)?;
if let Some(base) = base {
let base = self.resolve(base)?;
if let Some(arcs) = arcs {
Some(format!("{}.{}", base, arcs))
} else {
Some(base)
}
} else {
arcs.clone()
}
}

pub fn iter(&self) -> impl '_ + Iterator<Item = (String, String, String)> {
let bases: HashSet<&String> = self
.tree
.values()
.filter_map(|(base, ..)| base.as_ref())
.collect();

self.tree
.keys()
.filter(move |n| !bases.contains(n))
.filter_map(|n| self.resolve(n).map(|p| (self.spec.clone(), n.clone(), p)))
}
}

#[test]
fn test() {
let asn1 = super::Asn1Parser::new(
"none".into(),
r"
foo OBJECT IDENTIFIER ::= { bar(1) baz(2) 3 }
bat OBJECT IDENTIFIER ::= { foo qux(4) 5 }
quz OBJECT IDENTIFIER ::= { bat 6 }
",
);

let answer = (
"none".to_string(),
"quz".to_string(),
"1.2.3.4.5.6".to_string(),
);

let mut iter = asn1.iter();
assert_eq!(Some(answer), iter.next());
assert_eq!(None, iter.next());
}
46 changes: 0 additions & 46 deletions const-oid/oiddbgen/src/kind.rs

This file was deleted.

28 changes: 28 additions & 0 deletions const-oid/oiddbgen/src/ldap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#[derive(Clone, Debug, Default)]
pub struct LdapParser<'a>(&'a str);

impl<'a> LdapParser<'a> {
pub fn new(ldap: &'a str) -> Self {
Self(ldap)
}

pub fn iter(&self) -> impl '_ + Iterator<Item = (String, String, String)> {
self.0.lines().filter_map(|line| {
let (name, next) = line.split_at(line.find(',').unwrap());
let (.., next) = next[1..].split_at(next[1..].find(',').unwrap());
let (obid, spec) = next[1..].split_at(next[1..].find(',').unwrap());

let indx = obid.find('.')?;
obid.split_at(indx).0.parse::<usize>().ok()?;

if !spec.trim().starts_with(",[RFC") {
return None;
}

let spec = spec[2..][..spec.len() - 3].to_ascii_lowercase();
let name = name.trim().to_string();
let obid = obid.trim().to_string();
Some((spec, name, obid))
})
}
}
5 changes: 4 additions & 1 deletion const-oid/oiddbgen/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
mod kind;
mod asn1;
mod ldap;
mod node;
mod root;
mod spec;

pub use asn1::Asn1Parser;
pub use ldap::LdapParser;
pub use root::Root;
24 changes: 5 additions & 19 deletions const-oid/oiddbgen/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use oiddbgen::Root;
use oiddbgen::{Asn1Parser, LdapParser, Root};

// Update this database by downloading the CSV file here:
// https://www.iana.org/assignments/ldap-parameters/ldap-parameters.xhtml#ldap-parameters-3
Expand All @@ -8,29 +8,15 @@ const LDAP: &str = include_str!("../ldap-parameters-3.csv");
// https://www.rfc-editor.org/rfc/rfc5280.txt
const RFC5280: &str = include_str!("../rfc5280.txt");

fn rfc5280() -> impl Iterator<Item = String> {
const RE: &str = "^(id-ce-[a-zA-Z0-9-]+) +OBJECT +IDENTIFIER *::= *\\{ *id-ce +(\\d+) *\\}";

let re = regex::Regex::new(RE).unwrap();
RFC5280
.lines()
.filter_map(move |line| re.captures(line))
.map(|cap| {
let obji = format!("2.5.29.{}", cap.get(2).unwrap().as_str());
let name = cap.get(1).unwrap().as_str();
format!("{},X,{},[RFC5280]", name, obji)
})
}

fn main() {
let mut root = Root::default();

for line in LDAP.lines().skip(1) {
root.parse_line(line);
for (spec, name, obid) in LdapParser::new(LDAP).iter() {
root.add(&spec, &name, &obid)
}

for line in rfc5280() {
root.parse_line(&line);
for (spec, name, obid) in Asn1Parser::new("rfc5280".into(), RFC5280).iter() {
root.add(&spec, &name, &obid)
}

println!("{}", root.module());
Expand Down
10 changes: 5 additions & 5 deletions const-oid/oiddbgen/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ use quote::quote;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Node {
obji: String,
obid: String,
name: String,
symb: Ident,
}

impl Node {
pub fn new(obji: String, name: String) -> Self {
pub fn new(obid: String, name: String) -> Self {
// Raise the first letter in the beginning or after a hyphen.
// This produces more natural UpperSnake conversions below.
let mut upper = true;
Expand All @@ -31,21 +31,21 @@ impl Node {
let symb = symb.to_case(Case::UpperSnake);
let symb = Ident::new(&symb, Span::call_site());

Self { obji, name, symb }
Self { obid, name, symb }
}

pub fn symbol(&self) -> &Ident {
&self.symb
}

pub fn definition(&self) -> TokenStream {
let obji = self.obji.replace(' ', ""); // Fix a typo.
let obid = self.obid.replace(' ', ""); // Fix a typo.
let symb = &self.symb;
let name = &self.name;

quote! {
pub const #symb: crate::NamedOid<'_> = crate::NamedOid {
oid: crate::ObjectIdentifier::new(#obji),
oid: crate::ObjectIdentifier::new(#obid),
name: #name,
};
}
Expand Down
81 changes: 17 additions & 64 deletions const-oid/oiddbgen/src/root.rs
Original file line number Diff line number Diff line change
@@ -1,80 +1,33 @@
use crate::{kind::Kind, node::Node, spec::Spec};
use crate::{node::Node, spec::Spec};

use std::collections::{BTreeMap, HashMap};
use std::collections::BTreeMap;

use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;

#[derive(Clone, Debug)]
pub struct Root {
tree: BTreeMap<u8, Kind>,
docs: HashMap<u8, (Ident, TokenStream)>,
}

impl Default for Root {
fn default() -> Self {
let mut docs = HashMap::new();

for (c, name, desc) in Self::DOCS {
let name = Ident::new(name, Span::call_site());
let desc = quote! { #![doc = #desc] };
docs.insert(*c, (name, desc));
}

let tree = BTreeMap::new();
Self { tree, docs }
}
}
#[derive(Clone, Debug, Default)]
pub struct Root(BTreeMap<Ident, Spec>);

impl Root {
const DOCS: &'static [(u8, &'static str, &'static str)] = &[
// From IANA
(b'A', "attr", "Attribute Type"),
(b'C', "dit", "DIT Content Rule"),
(b'E', "url", "LDAP URL Extension"),
(b'F', "family", "Family"),
(b'M', "matching", "Matching Rule"),
(b'N', "name", "Name Form"),
(b'O', "obj", "Object Class"),
(b'R', "admin", "Administrative Role"),
// Custom Additions
(b'X', "ext", "X.509 Certificate Extensions"),
];

pub fn parse_line(&mut self, line: &str) {
let (name, next) = line.split_at(line.find(',').unwrap());
let (kind, next) = next[1..].split_at(next[1..].find(',').unwrap());
let (obji, spec) = next[1..].split_at(next[1..].find(',').unwrap());

let arc: Option<usize> = obji.find('.').and_then(|i| obji.split_at(i).0.parse().ok());
if arc.is_some() && spec.trim().starts_with(",[RFC") {
let name = name.trim().to_string();
let kind = kind.trim().as_bytes()[0];
let obji = obji.trim().to_string();
let spec = Ident::new(
&spec[2..][..spec.len() - 3].to_ascii_lowercase(),
Span::call_site(),
);

if self.docs.contains_key(&kind) {
self.tree
.entry(kind)
.or_insert_with(Kind::default)
.entry(spec)
.or_insert_with(Spec::default)
.push(Node::new(obji, name));
}
}
pub fn add(&mut self, spec: &str, name: &str, obid: &str) {
let name = name.trim().to_string();
let obid = obid.trim().to_string();
let spec = spec.trim().to_ascii_lowercase();
let spec = Ident::new(&spec, Span::call_site());

self.0
.entry(spec)
.or_insert_with(Spec::default)
.push(Node::new(obid, name));
}

pub fn module(&self) -> TokenStream {
let mut mods = TokenStream::default();
let mut syms = TokenStream::default();

for (kind, k) in self.tree.iter() {
let (kind, docs) = self.docs.get(kind).unwrap();
mods.extend(k.module(kind, docs));
syms.extend(k.symbols(quote! { &#kind }));
for (spec, s) in &self.0 {
mods.extend(s.module(spec));
syms.extend(s.symbols(quote! { &#spec }));
}

quote! {
Expand Down
Loading