blob: 384963aa9a9012d8e7f8a63892b23589e6804b52 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::hash::Hash;
use std::time::SystemTime;
use rand::RngCore;
pub struct IDGenerator {}
impl IDGenerator {
/// ID generated by 3 parts
/// 1. Registered service instance id
/// 2. thread local level random u64
/// 3. Timestamp in ms
pub fn new_id(instance_id: i32) -> ID {
ID::new(
instance_id as i64,
rand::thread_rng().next_u64() as i64,
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).ok().unwrap().as_millis() as i64,
)
}
}
/// ID is used for trace id and segment id.
/// It is combined by 3 i64 numbers, and could be formatted as `part1.part2.part3` string.
#[derive(Clone, Hash)]
pub struct ID {
part1: i64,
part2: i64,
part3: i64,
}
impl ID {
pub fn new(part1: i64, part2: i64, part3: i64) -> Self {
ID {
part1,
part2,
part3,
}
}
/// Convert the literal string text back to ID object.
/// Return Option::None if the text is not combined by 3 dot split i64 parts
pub fn from(id_text: String) -> Result<Self, String> {
let strings: Vec<&str> = id_text.split(".").collect();
if strings.len() == 3 {
let part1 = strings[0].parse::<i64>();
if part1.is_err() { return Err("part 1 is not a i64".to_string()); }
let part2 = strings[1].parse::<i64>();
if part2.is_err() { return Err("part 2 is not a i64".to_string()); }
let part3 = strings[2].parse::<i64>();
if part3.is_err() { return Err("part 3 is not a i64".to_string()); }
Ok(ID::new(part1.unwrap(), part2.unwrap(), part3.unwrap()))
} else {
Err("The ID is not combined by 3 parts.".to_string())
}
}
}
impl PartialEq for ID {
fn eq(&self, other: &Self) -> bool {
self.part1 == other.part1 && self.part2 == other.part2 && self.part3 == other.part3
}
}
impl ToString for ID {
fn to_string(&self) -> String {
format!("{}.{}.{}", self.part1, self.part2, self.part3)
}
}
#[cfg(test)]
mod id_tests {
use crate::skywalking::core::ID;
use crate::skywalking::core::id::IDGenerator;
#[test]
fn test_id_generator() {
let id = IDGenerator::new_id(1);
assert_eq!(id.part1, 1);
}
#[test]
fn test_id_new() {
let id1 = ID::new(1, 2, 3);
let id2 = ID::new(1, 2, 3);
let id3 = ID::new(1, 2, 4);
assert_eq!(id1.eq(&id2), true);
assert_eq!(id1.ne(&id3), true);
assert_eq!(id1.to_string(), "1.2.3");
let id4 = ID::from(String::from("1.2.3")).unwrap();
assert_eq!(id4.eq(&id1), true);
let id5_none = ID::from(String::from("1.2"));
assert_ne!(id5_none.err().unwrap().len(), 0);
let id6_illegal = ID::from(String::from("1.2.a"));
assert_ne!(id6_illegal.err().unwrap().len(), 0);
}
}