| // 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); |
| } |
| } |