mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
Builtin UUID module (#68)
* Implement builtin `uuid.parse` method * Implement builtin `uuid.rfc4122` method * Parse timestamps for v2 UUIDs
This commit is contained in:
+5
-1
@@ -28,6 +28,8 @@ mod tracing;
|
||||
pub mod types;
|
||||
mod units;
|
||||
mod utils;
|
||||
#[cfg(feature = "uuid")]
|
||||
mod uuid;
|
||||
|
||||
use crate::ast::{Expr, Ref};
|
||||
use crate::lexer::Span;
|
||||
@@ -77,7 +79,8 @@ lazy_static! {
|
||||
//graphql::register(&mut m);
|
||||
//http::register(&mut m);
|
||||
//net::register(&mut m);
|
||||
//uuid::register(&mut m);
|
||||
#[cfg(feature = "uuid")]
|
||||
uuid::register(&mut m);
|
||||
#[cfg(feature = "semver")]
|
||||
semver::register(&mut m);
|
||||
//rego::register(&mut m);
|
||||
@@ -92,6 +95,7 @@ lazy_static! {
|
||||
pub fn must_cache(path: &str) -> Option<&'static str> {
|
||||
match path {
|
||||
"rand.intn" => Some("rand.intn"),
|
||||
"uuid.rfc4122" => Some("uuid.rfc4122"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use crate::ast::{Expr, Ref};
|
||||
use crate::builtins;
|
||||
use crate::builtins::utils::{ensure_args_count, ensure_string};
|
||||
use crate::lexer::Span;
|
||||
use crate::value::Value;
|
||||
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
use anyhow::{Ok, Result};
|
||||
use uuid::{Timestamp, Uuid};
|
||||
|
||||
pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("uuid.parse", (parse, 1));
|
||||
m.insert("uuid.rfc4122", (rfc4122, 1));
|
||||
}
|
||||
|
||||
fn parse(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "uuid.parse";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
|
||||
let val = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
let Some(uuid) = Uuid::parse_str(&val).ok() else {
|
||||
return Ok(Value::Undefined);
|
||||
};
|
||||
let version = uuid.get_version_num();
|
||||
|
||||
let mut result = BTreeMap::new();
|
||||
result.insert(
|
||||
Value::String("version".into()),
|
||||
Value::Number(version.into()),
|
||||
);
|
||||
result.insert(
|
||||
Value::String("variant".into()),
|
||||
Value::String(uuid.get_variant().to_string().into()),
|
||||
);
|
||||
|
||||
if let Some(time) = timestamp(&uuid) {
|
||||
let (sec, nanosec) = time.to_unix();
|
||||
let time = sec.wrapping_mul(1_000_000_000).wrapping_add(nanosec as u64);
|
||||
result.insert(Value::String("time".into()), Value::Number(time.into()));
|
||||
}
|
||||
|
||||
if version == 1 || version == 2 {
|
||||
let (f1, _, _, f4) = uuid.as_fields();
|
||||
|
||||
result.insert(
|
||||
Value::String("nodeid".into()),
|
||||
Value::String(
|
||||
f4[2..]
|
||||
.iter()
|
||||
.map(|f| format!("{f:02x}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("-")
|
||||
.into(),
|
||||
),
|
||||
);
|
||||
result.insert(
|
||||
Value::String("macvariables".into()),
|
||||
Value::String(mac_vars(f4[2]).into()),
|
||||
);
|
||||
|
||||
let clock_seq = u16::from_be_bytes([f4[0], f4[1]]) & 0x3fff;
|
||||
result.insert(
|
||||
Value::String("clocksequence".into()),
|
||||
Value::Number((clock_seq as u64).into()),
|
||||
);
|
||||
|
||||
if version == 2 {
|
||||
result.insert(
|
||||
Value::String("id".into()),
|
||||
Value::Number((f1 as u64).into()),
|
||||
);
|
||||
result.insert(
|
||||
Value::String("domain".into()),
|
||||
Value::String(domain(f4[1]).into()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::from(result))
|
||||
}
|
||||
|
||||
fn rfc4122(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "uuid.rfc4122";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
ensure_string(name, ¶ms[0], &args[0])?;
|
||||
|
||||
let uuid = Uuid::new_v4();
|
||||
Ok(Value::String(uuid.to_string().into()))
|
||||
}
|
||||
|
||||
fn mac_vars(b: u8) -> &'static str {
|
||||
if b & 0b11 == 0b11 {
|
||||
return "local:multicast";
|
||||
} else if b & 0b01 == 0b01 {
|
||||
return "global:multicast";
|
||||
} else if b & 0b10 == 0b10 {
|
||||
return "local:unicast";
|
||||
}
|
||||
"global:unicast"
|
||||
}
|
||||
|
||||
fn domain(b: u8) -> String {
|
||||
match b {
|
||||
0 => "Person".to_string(),
|
||||
1 => "Group".to_string(),
|
||||
2 => "Org".to_string(),
|
||||
n => format!("Domain{n}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn timestamp(uuid: &Uuid) -> Option<Timestamp> {
|
||||
// We need a special case for v2 UUIDs because `uuid` crate does not support
|
||||
// parsing timestamps for v2 UUIDs but OPA tests expects them and also
|
||||
// the original Go implementation parses timestamps for v2 UUIDs.
|
||||
// This is just a copy of parsing logic for v1 UUIDs:
|
||||
// https://github.com/uuid-rs/uuid/blob/94ecea893fadac93248f1bd6f47673c09cec5912/src/lib.rs#L900-L904
|
||||
if uuid.get_version_num() == 2 {
|
||||
let (ticks, counter) = decode_rfc4122_timestamp(uuid);
|
||||
return Some(Timestamp::from_rfc4122(ticks, counter));
|
||||
}
|
||||
|
||||
uuid.get_timestamp()
|
||||
}
|
||||
|
||||
// Copied from https://github.com/uuid-rs/uuid/blob/94ecea893fadac93248f1bd6f47673c09cec5912/src/timestamp.rs#L190-L205
|
||||
const fn decode_rfc4122_timestamp(uuid: &Uuid) -> (u64, u16) {
|
||||
let bytes = uuid.as_bytes();
|
||||
|
||||
let ticks: u64 = ((bytes[6] & 0x0F) as u64) << 56
|
||||
| (bytes[7] as u64) << 48
|
||||
| (bytes[4] as u64) << 40
|
||||
| (bytes[5] as u64) << 32
|
||||
| (bytes[0] as u64) << 24
|
||||
| (bytes[1] as u64) << 16
|
||||
| (bytes[2] as u64) << 8
|
||||
| (bytes[3] as u64);
|
||||
|
||||
let counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16);
|
||||
|
||||
(ticks, counter)
|
||||
}
|
||||
Reference in New Issue
Block a user