Make SqlState into an opaque type rather than enum

This commit is contained in:
Steven Fackler
2017-07-08 20:52:36 -07:00
parent 6f0950b45b
commit 01a1529624
11 changed files with 1094 additions and 1131 deletions

View File

@@ -2,29 +2,22 @@ use std::fs::File;
use std::io::{Write, BufWriter};
use std::path::Path;
use phf_codegen;
use snake_to_camel;
use linked_hash_map::LinkedHashMap;
const ERRCODES_TXT: &'static str = include_str!("errcodes.txt");
struct Code {
code: String,
variant: String,
}
pub fn build(path: &Path) {
let mut file = BufWriter::new(File::create(path.join("error/sqlstate.rs")).unwrap());
let codes = parse_codes();
make_header(&mut file);
make_enum(&codes, &mut file);
make_type(&mut file);
make_consts(&codes, &mut file);
make_map(&codes, &mut file);
make_impl(&codes, &mut file);
}
fn parse_codes() -> Vec<Code> {
let mut codes = vec![];
fn parse_codes() -> LinkedHashMap<String, Vec<String>> {
let mut codes = LinkedHashMap::new();
for line in ERRCODES_TXT.lines() {
if line.starts_with("#") || line.starts_with("Section") || line.trim().is_empty() {
@@ -34,131 +27,70 @@ fn parse_codes() -> Vec<Code> {
let mut it = line.split_whitespace();
let code = it.next().unwrap().to_owned();
it.next();
it.next();
// for 2202E
let name = match it.next() {
Some(name) => name,
None => continue,
};
let variant = match variant_name(&code) {
Some(variant) => variant,
None => snake_to_camel(&name),
};
let name = it.next().unwrap().replace("ERRCODE_", "");
codes.push(Code {
code: code,
variant: variant,
});
codes.entry(code).or_insert_with(Vec::new).push(name);
}
codes
}
fn variant_name(code: &str) -> Option<String> {
match code {
"01004" => Some("WarningStringDataRightTruncation".to_owned()),
"22001" => Some("DataStringDataRightTruncation".to_owned()),
"2F002" => Some("SqlRoutineModifyingSqlDataNotPermitted".to_owned()),
"38002" => Some("ForeignRoutineModifyingSqlDataNotPermitted".to_owned()),
"2F003" => Some("SqlRoutineProhibitedSqlStatementAttempted".to_owned()),
"38003" => Some("ForeignRoutineProhibitedSqlStatementAttempted".to_owned()),
"2F004" => Some("SqlRoutineReadingSqlDataNotPermitted".to_owned()),
"38004" => Some("ForeignRoutineReadingSqlDataNotPermitted".to_owned()),
"22004" => Some("DataNullValueNotAllowed".to_owned()),
"39004" => Some("ExternalRoutineInvocationNullValueNotAllowed".to_owned()),
_ => None,
}
}
fn make_header(file: &mut BufWriter<File>) {
fn make_type(file: &mut BufWriter<File>) {
write!(
file,
"// Autogenerated file - DO NOT EDIT
use phf;
use std::borrow::Cow;
"
).unwrap();
}
fn make_enum(codes: &[Code], file: &mut BufWriter<File>) {
write!(
file,
r#"/// SQLSTATE error codes
/// A SQLSTATE error code
#[derive(PartialEq, Eq, Clone, Debug)]
#[allow(enum_variant_names)]
pub enum SqlState {{
"#
).unwrap();
pub struct SqlState(Cow<'static, str>);
for code in codes {
write!(
file,
" /// `{}`
{},\n",
code.code,
code.variant
).unwrap();
}
write!(
file,
" /// An unknown code
Other(String),
}}
"
).unwrap();
}
fn make_map(codes: &[Code], file: &mut BufWriter<File>) {
write!(
file,
"#[cfg_attr(rustfmt, rustfmt_skip)]
static SQLSTATE_MAP: phf::Map<&'static str, SqlState> = "
).unwrap();
let mut builder = phf_codegen::Map::new();
for code in codes {
builder.entry(&*code.code, &format!("SqlState::{}", code.variant));
}
builder.build(file).unwrap();
write!(file, ";\n").unwrap();
}
fn make_impl(codes: &[Code], file: &mut BufWriter<File>) {
write!(
file,
r#"
impl SqlState {{
/// Creates a `SqlState` from its error code.
pub fn from_code(s: &str) -> SqlState {{
match SQLSTATE_MAP.get(s) {{
Some(state) => state.clone(),
None => SqlState::Other(s.to_owned()),
None => SqlState(Cow::Owned(s.to_string())),
}}
}}
/// Returns the error code corresponding to the `SqlState`.
pub fn code(&self) -> &str {{
match *self {{"#
).unwrap();
for code in codes {
write!(
file,
r#"
SqlState::{} => "{}","#,
code.variant,
code.code
).unwrap();
}
write!(
file,
r#"
SqlState::Other(ref s) => s,
}}
&self.0
}}
}}
"#
"
).unwrap();
}
fn make_consts(codes: &LinkedHashMap<String, Vec<String>>, file: &mut BufWriter<File>) {
for (code, names) in codes {
for name in names {
write!(
file,
r#"
/// {code}
pub const {name}: SqlState = SqlState(Cow::Borrowed("{code}"));
"#,
name = name,
code = code,
).unwrap();
}
}
}
fn make_map(codes: &LinkedHashMap<String, Vec<String>>, file: &mut BufWriter<File>) {
write!(
file,
"
#[cfg_attr(rustfmt, rustfmt_skip)]
static SQLSTATE_MAP: phf::Map<&'static str, SqlState> = "
).unwrap();
let mut builder = phf_codegen::Map::new();
for (code, names) in codes {
builder.entry(&**code, &names[0]);
}
builder.build(file).unwrap();
write!(file, ";\n").unwrap();
}