Sanitize and build data structure from Consul configurationLoading site configuration from ini fileConfiguration concept and implementationNode.JS module to retrieve configuration from environment variablesLoading configuration properties from XMLReading and writing configuration fileA class to load, check and supply configuration data from a .py fileApp that sets configuration dataEditing a configuration file using C++ and Win32
What does "綺麗" mean in this sentence?
Pass a bash variable to python script
Novel set in the future, children cannot change the class they are born into, one class is made uneducated by associating books with pain
7 mentions of night in Gospel of John
Where does the tea come from?
Does the warlock's Gift of the Ever-Living Ones eldritch invocation work with potions or healing spells cast on you by others?
Extra battery in the gap of an HDD
Marxist and post modernism contradiction
d-Menthol vs dl-menthol: Does an enantiomer and its racemic mixture have different melting points?
Is there any restriction in entering the South American countries multiple times in one year?
Where is the 'zone of reversed commands...'?
Is a light year a different distance if measured from a moving object?
Do you say "good game" after a game in which your opponent played poorly?
Using Terminal` (ASCII plots) in Wolfram 12
What is the name for a fluid transition between two tones? When did it first appear?
Can digital computers understand infinity?
Dynamics m, r, s, and z. What do they mean?
Proofreading a novel: is it okay to use a question mark with an exclamation mark - "?!"
Can you take an Immortal Phoenix out of the game?
Some interesting and elementary topics with connections to the representation theory?
Do I need to explicitly handle negative numbers or zero when summing squared digits?
counter in hexadecimal base
A fast aquatic predator with multiple eyes and pupils. Would these eyes be possible?
Does a reincarnated Draconic Bloodline Sorcerer save his class abilities?
Sanitize and build data structure from Consul configuration
Loading site configuration from ini fileConfiguration concept and implementationNode.JS module to retrieve configuration from environment variablesLoading configuration properties from XMLReading and writing configuration fileA class to load, check and supply configuration data from a .py fileApp that sets configuration dataEditing a configuration file using C++ and Win32
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty
margin-bottom:0;
$begingroup$
I have a piece of working code that takes some strings from Consul configuration exports, sanitizes them a little and converts them into valid json and/or hocon structures.
While I am iterating over a vector of Nodes
in the process
function, I couldn't come up with a better solution than having to clone
them. After inspecting some related APIs and looking for possible solutions, I understand (hopefully correctly) that it's a baked-in feature of Rust that you cannot move the ownership of the 'iteratee' to the result of the iteration.
Probably, I have to re-write everything in order to optimize the performance, but I need to research more to understand what would be the most idiomatic way to write this in Rust.
In the process
I'm cloning the Vec
's of Node
's. I want to collect them as Node
's, and not as &Node
's, and this doesn't play well with the ownership transfer.
I apologize for my shallow Rust knowledge in advance.
pub struct ConsulKV
pub Key: String,
pub Value: Option<String>
pub struct RawKV
pub key: String,
pub value: String
struct Location
ns: Vec<String>,
impl Location
fn path(&self) -> String
self.ns.join("/")
fn name(&self) -> String
self.ns.last().get_or_insert(&String::from(".")).clone()
fn base(&self) -> Location
match self.ns.first()
None => Location ns: vec![] ,
Some(head) => Location
ns: vec![head.clone()],
,
fn drop_base_mut(&mut self) -> &Location
self.ns.remove(0);
self
impl PartialEq for Location
fn eq(&self, other: &Location) -> bool
self.ns == other.ns
pub enum Node
KeyValue
path: Location,
name: String,
value: String,
,
Directory
path: Location,
nodes: Vec<Node>,
,
impl Node
pub fn new(r1: Vec<RawKV>) -> ResulT<Vec<Node>>
let raws = Node::preprocess(r1)?;
Ok(Node::process(raws))
fn preprocess(raw_kvs: Vec<RawKV>) -> ResulT<Vec<Node>> rkv
fn process(mut nodes: Vec<Node>) -> Vec<Node> n.path().base());
grouped
.into_iter()
.map(
fn drop_base_mut(&mut self) -> &Node
match self
Node::KeyValue path, .. =>
path.drop_base_mut();
self
Node::Directory path, .. =>
path.drop_base_mut();
self
fn path(&self) -> &Location
match self
Node::KeyValue path, .. => path,
Node::Directory path, .. => path,
performance beginner rust configuration
New contributor
$endgroup$
add a comment
|
$begingroup$
I have a piece of working code that takes some strings from Consul configuration exports, sanitizes them a little and converts them into valid json and/or hocon structures.
While I am iterating over a vector of Nodes
in the process
function, I couldn't come up with a better solution than having to clone
them. After inspecting some related APIs and looking for possible solutions, I understand (hopefully correctly) that it's a baked-in feature of Rust that you cannot move the ownership of the 'iteratee' to the result of the iteration.
Probably, I have to re-write everything in order to optimize the performance, but I need to research more to understand what would be the most idiomatic way to write this in Rust.
In the process
I'm cloning the Vec
's of Node
's. I want to collect them as Node
's, and not as &Node
's, and this doesn't play well with the ownership transfer.
I apologize for my shallow Rust knowledge in advance.
pub struct ConsulKV
pub Key: String,
pub Value: Option<String>
pub struct RawKV
pub key: String,
pub value: String
struct Location
ns: Vec<String>,
impl Location
fn path(&self) -> String
self.ns.join("/")
fn name(&self) -> String
self.ns.last().get_or_insert(&String::from(".")).clone()
fn base(&self) -> Location
match self.ns.first()
None => Location ns: vec![] ,
Some(head) => Location
ns: vec![head.clone()],
,
fn drop_base_mut(&mut self) -> &Location
self.ns.remove(0);
self
impl PartialEq for Location
fn eq(&self, other: &Location) -> bool
self.ns == other.ns
pub enum Node
KeyValue
path: Location,
name: String,
value: String,
,
Directory
path: Location,
nodes: Vec<Node>,
,
impl Node
pub fn new(r1: Vec<RawKV>) -> ResulT<Vec<Node>>
let raws = Node::preprocess(r1)?;
Ok(Node::process(raws))
fn preprocess(raw_kvs: Vec<RawKV>) -> ResulT<Vec<Node>> rkv
fn process(mut nodes: Vec<Node>) -> Vec<Node> n.path().base());
grouped
.into_iter()
.map(
fn drop_base_mut(&mut self) -> &Node
match self
Node::KeyValue path, .. =>
path.drop_base_mut();
self
Node::Directory path, .. =>
path.drop_base_mut();
self
fn path(&self) -> &Location
match self
Node::KeyValue path, .. => path,
Node::Directory path, .. => path,
performance beginner rust configuration
New contributor
$endgroup$
add a comment
|
$begingroup$
I have a piece of working code that takes some strings from Consul configuration exports, sanitizes them a little and converts them into valid json and/or hocon structures.
While I am iterating over a vector of Nodes
in the process
function, I couldn't come up with a better solution than having to clone
them. After inspecting some related APIs and looking for possible solutions, I understand (hopefully correctly) that it's a baked-in feature of Rust that you cannot move the ownership of the 'iteratee' to the result of the iteration.
Probably, I have to re-write everything in order to optimize the performance, but I need to research more to understand what would be the most idiomatic way to write this in Rust.
In the process
I'm cloning the Vec
's of Node
's. I want to collect them as Node
's, and not as &Node
's, and this doesn't play well with the ownership transfer.
I apologize for my shallow Rust knowledge in advance.
pub struct ConsulKV
pub Key: String,
pub Value: Option<String>
pub struct RawKV
pub key: String,
pub value: String
struct Location
ns: Vec<String>,
impl Location
fn path(&self) -> String
self.ns.join("/")
fn name(&self) -> String
self.ns.last().get_or_insert(&String::from(".")).clone()
fn base(&self) -> Location
match self.ns.first()
None => Location ns: vec![] ,
Some(head) => Location
ns: vec![head.clone()],
,
fn drop_base_mut(&mut self) -> &Location
self.ns.remove(0);
self
impl PartialEq for Location
fn eq(&self, other: &Location) -> bool
self.ns == other.ns
pub enum Node
KeyValue
path: Location,
name: String,
value: String,
,
Directory
path: Location,
nodes: Vec<Node>,
,
impl Node
pub fn new(r1: Vec<RawKV>) -> ResulT<Vec<Node>>
let raws = Node::preprocess(r1)?;
Ok(Node::process(raws))
fn preprocess(raw_kvs: Vec<RawKV>) -> ResulT<Vec<Node>> rkv
fn process(mut nodes: Vec<Node>) -> Vec<Node> n.path().base());
grouped
.into_iter()
.map(
fn drop_base_mut(&mut self) -> &Node
match self
Node::KeyValue path, .. =>
path.drop_base_mut();
self
Node::Directory path, .. =>
path.drop_base_mut();
self
fn path(&self) -> &Location
match self
Node::KeyValue path, .. => path,
Node::Directory path, .. => path,
performance beginner rust configuration
New contributor
$endgroup$
I have a piece of working code that takes some strings from Consul configuration exports, sanitizes them a little and converts them into valid json and/or hocon structures.
While I am iterating over a vector of Nodes
in the process
function, I couldn't come up with a better solution than having to clone
them. After inspecting some related APIs and looking for possible solutions, I understand (hopefully correctly) that it's a baked-in feature of Rust that you cannot move the ownership of the 'iteratee' to the result of the iteration.
Probably, I have to re-write everything in order to optimize the performance, but I need to research more to understand what would be the most idiomatic way to write this in Rust.
In the process
I'm cloning the Vec
's of Node
's. I want to collect them as Node
's, and not as &Node
's, and this doesn't play well with the ownership transfer.
I apologize for my shallow Rust knowledge in advance.
pub struct ConsulKV
pub Key: String,
pub Value: Option<String>
pub struct RawKV
pub key: String,
pub value: String
struct Location
ns: Vec<String>,
impl Location
fn path(&self) -> String
self.ns.join("/")
fn name(&self) -> String
self.ns.last().get_or_insert(&String::from(".")).clone()
fn base(&self) -> Location
match self.ns.first()
None => Location ns: vec![] ,
Some(head) => Location
ns: vec![head.clone()],
,
fn drop_base_mut(&mut self) -> &Location
self.ns.remove(0);
self
impl PartialEq for Location
fn eq(&self, other: &Location) -> bool
self.ns == other.ns
pub enum Node
KeyValue
path: Location,
name: String,
value: String,
,
Directory
path: Location,
nodes: Vec<Node>,
,
impl Node
pub fn new(r1: Vec<RawKV>) -> ResulT<Vec<Node>>
let raws = Node::preprocess(r1)?;
Ok(Node::process(raws))
fn preprocess(raw_kvs: Vec<RawKV>) -> ResulT<Vec<Node>> rkv
fn process(mut nodes: Vec<Node>) -> Vec<Node> n.path().base());
grouped
.into_iter()
.map(
fn drop_base_mut(&mut self) -> &Node
match self
Node::KeyValue path, .. =>
path.drop_base_mut();
self
Node::Directory path, .. =>
path.drop_base_mut();
self
fn path(&self) -> &Location
match self
Node::KeyValue path, .. => path,
Node::Directory path, .. => path,
performance beginner rust configuration
performance beginner rust configuration
New contributor
New contributor
edited 7 hours ago
200_success
136k21 gold badges175 silver badges447 bronze badges
136k21 gold badges175 silver badges447 bronze badges
New contributor
asked 11 hours ago
RedDreeRedDree
164 bronze badges
164 bronze badges
New contributor
New contributor
add a comment
|
add a comment
|
1 Answer
1
active
oldest
votes
$begingroup$
You're doing multiple clone operations because of the types you've used. Due to the fact that consul's KV store is a tree, it does not really make that much sense to represent it as a set of nested Vec
s. As I'm sure you found out, you're running into serious issues trying to figure out whether keys are inserted, what keys are inserted, and how to modify them without getting the borrow checker in a twist.
I'd like to share with you a simpler implementation, one that sidesteps all the problems you've had entirely. Since we're dealing with a KV tree, we can and should take advantage of a HashMap
for this - its type is absolutely ideal for what we are about to do. We're going to define two enum types, one for the keys, one for the value:
#[derive(Eq, PartialEq, Hash, Debug)]
pub enum Key
Leaf(String),
Branch(String)
#[derive(Eq, PartialEq, Debug)]
pub enum Node
Leaf
key: String,
value: String
,
Branch
key: String,
children: HashMap<Key, Node>
We're going to then proceed through a little game of indirection to avoid some of the reference issues we might encounter...
impl Node
fn insert_key(children: &mut HashMap<Key, Node>, key: String, value: String)
match children.entry(Key::Leaf(key.clone()))
Entry::Occupied(mut state) =>
state.insert(Node::Leaf
key: key,
value: value
);
,
Entry::Vacant(state) =>
state.insert(Node::Leaf
key: key,
value: value
);
fn branch(children: &mut HashMap<Key, Node>, key: String, remainder: Vec<String>, value: String)
match children.entry(Key::Branch(key.clone()))
Entry::Occupied(mut state) =>
// We already have a branch of that name, we just
// forward the call and move on
state.get_mut().add_value(remainder, value)
,
Entry::Vacant(state) =>
// We need to create the node
let mut node = Node::Branch
key: key,
children: HashMap::new()
;
let status = node.add_value(remainder, value);
state.insert(node);
status
;
pub fn get(&self, test: &Key) -> Option<&Node>
match self
Node::Branch
key: _key,
ref children
=> children.get(test),
_ => None
pub fn add_value(&mut self, mut path: Vec<String>, value: String)
match path.len()
0 => panic!("Path cannot be empty"),
1 => Node::insert_key(contents, path.pop().unwrap(), value),
_ => Node::branch(contents, path.pop().unwrap(), path, value)
);
And finally, we create a method to construct our tree:
pub fn into_tree(collection: Vec<RawKV>) -> Node
// Create the root namespace
println!("Creating nodes");
let mut root_node = Node::Branch
key: "/".to_string(),
children: HashMap::new()
;
for node in collection
let mut path_elements:Vec<String> = node.key.split("/").map(
root_node
This is more efficient than your method in multiple respects:
- Less allocations (I am not copying nodes unless I absolutely have to)
- Less re-processing (no use of
group_by
, nopartition
, nothing but recursive tree access) - Clearer code
Let me know what you think :-)
$endgroup$
add a comment
|
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/4.0/"u003ecc by-sa 4.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
RedDree is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f230267%2fsanitize-and-build-data-structure-from-consul-configuration%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
You're doing multiple clone operations because of the types you've used. Due to the fact that consul's KV store is a tree, it does not really make that much sense to represent it as a set of nested Vec
s. As I'm sure you found out, you're running into serious issues trying to figure out whether keys are inserted, what keys are inserted, and how to modify them without getting the borrow checker in a twist.
I'd like to share with you a simpler implementation, one that sidesteps all the problems you've had entirely. Since we're dealing with a KV tree, we can and should take advantage of a HashMap
for this - its type is absolutely ideal for what we are about to do. We're going to define two enum types, one for the keys, one for the value:
#[derive(Eq, PartialEq, Hash, Debug)]
pub enum Key
Leaf(String),
Branch(String)
#[derive(Eq, PartialEq, Debug)]
pub enum Node
Leaf
key: String,
value: String
,
Branch
key: String,
children: HashMap<Key, Node>
We're going to then proceed through a little game of indirection to avoid some of the reference issues we might encounter...
impl Node
fn insert_key(children: &mut HashMap<Key, Node>, key: String, value: String)
match children.entry(Key::Leaf(key.clone()))
Entry::Occupied(mut state) =>
state.insert(Node::Leaf
key: key,
value: value
);
,
Entry::Vacant(state) =>
state.insert(Node::Leaf
key: key,
value: value
);
fn branch(children: &mut HashMap<Key, Node>, key: String, remainder: Vec<String>, value: String)
match children.entry(Key::Branch(key.clone()))
Entry::Occupied(mut state) =>
// We already have a branch of that name, we just
// forward the call and move on
state.get_mut().add_value(remainder, value)
,
Entry::Vacant(state) =>
// We need to create the node
let mut node = Node::Branch
key: key,
children: HashMap::new()
;
let status = node.add_value(remainder, value);
state.insert(node);
status
;
pub fn get(&self, test: &Key) -> Option<&Node>
match self
Node::Branch
key: _key,
ref children
=> children.get(test),
_ => None
pub fn add_value(&mut self, mut path: Vec<String>, value: String)
match path.len()
0 => panic!("Path cannot be empty"),
1 => Node::insert_key(contents, path.pop().unwrap(), value),
_ => Node::branch(contents, path.pop().unwrap(), path, value)
);
And finally, we create a method to construct our tree:
pub fn into_tree(collection: Vec<RawKV>) -> Node
// Create the root namespace
println!("Creating nodes");
let mut root_node = Node::Branch
key: "/".to_string(),
children: HashMap::new()
;
for node in collection
let mut path_elements:Vec<String> = node.key.split("/").map(
root_node
This is more efficient than your method in multiple respects:
- Less allocations (I am not copying nodes unless I absolutely have to)
- Less re-processing (no use of
group_by
, nopartition
, nothing but recursive tree access) - Clearer code
Let me know what you think :-)
$endgroup$
add a comment
|
$begingroup$
You're doing multiple clone operations because of the types you've used. Due to the fact that consul's KV store is a tree, it does not really make that much sense to represent it as a set of nested Vec
s. As I'm sure you found out, you're running into serious issues trying to figure out whether keys are inserted, what keys are inserted, and how to modify them without getting the borrow checker in a twist.
I'd like to share with you a simpler implementation, one that sidesteps all the problems you've had entirely. Since we're dealing with a KV tree, we can and should take advantage of a HashMap
for this - its type is absolutely ideal for what we are about to do. We're going to define two enum types, one for the keys, one for the value:
#[derive(Eq, PartialEq, Hash, Debug)]
pub enum Key
Leaf(String),
Branch(String)
#[derive(Eq, PartialEq, Debug)]
pub enum Node
Leaf
key: String,
value: String
,
Branch
key: String,
children: HashMap<Key, Node>
We're going to then proceed through a little game of indirection to avoid some of the reference issues we might encounter...
impl Node
fn insert_key(children: &mut HashMap<Key, Node>, key: String, value: String)
match children.entry(Key::Leaf(key.clone()))
Entry::Occupied(mut state) =>
state.insert(Node::Leaf
key: key,
value: value
);
,
Entry::Vacant(state) =>
state.insert(Node::Leaf
key: key,
value: value
);
fn branch(children: &mut HashMap<Key, Node>, key: String, remainder: Vec<String>, value: String)
match children.entry(Key::Branch(key.clone()))
Entry::Occupied(mut state) =>
// We already have a branch of that name, we just
// forward the call and move on
state.get_mut().add_value(remainder, value)
,
Entry::Vacant(state) =>
// We need to create the node
let mut node = Node::Branch
key: key,
children: HashMap::new()
;
let status = node.add_value(remainder, value);
state.insert(node);
status
;
pub fn get(&self, test: &Key) -> Option<&Node>
match self
Node::Branch
key: _key,
ref children
=> children.get(test),
_ => None
pub fn add_value(&mut self, mut path: Vec<String>, value: String)
match path.len()
0 => panic!("Path cannot be empty"),
1 => Node::insert_key(contents, path.pop().unwrap(), value),
_ => Node::branch(contents, path.pop().unwrap(), path, value)
);
And finally, we create a method to construct our tree:
pub fn into_tree(collection: Vec<RawKV>) -> Node
// Create the root namespace
println!("Creating nodes");
let mut root_node = Node::Branch
key: "/".to_string(),
children: HashMap::new()
;
for node in collection
let mut path_elements:Vec<String> = node.key.split("/").map(
root_node
This is more efficient than your method in multiple respects:
- Less allocations (I am not copying nodes unless I absolutely have to)
- Less re-processing (no use of
group_by
, nopartition
, nothing but recursive tree access) - Clearer code
Let me know what you think :-)
$endgroup$
add a comment
|
$begingroup$
You're doing multiple clone operations because of the types you've used. Due to the fact that consul's KV store is a tree, it does not really make that much sense to represent it as a set of nested Vec
s. As I'm sure you found out, you're running into serious issues trying to figure out whether keys are inserted, what keys are inserted, and how to modify them without getting the borrow checker in a twist.
I'd like to share with you a simpler implementation, one that sidesteps all the problems you've had entirely. Since we're dealing with a KV tree, we can and should take advantage of a HashMap
for this - its type is absolutely ideal for what we are about to do. We're going to define two enum types, one for the keys, one for the value:
#[derive(Eq, PartialEq, Hash, Debug)]
pub enum Key
Leaf(String),
Branch(String)
#[derive(Eq, PartialEq, Debug)]
pub enum Node
Leaf
key: String,
value: String
,
Branch
key: String,
children: HashMap<Key, Node>
We're going to then proceed through a little game of indirection to avoid some of the reference issues we might encounter...
impl Node
fn insert_key(children: &mut HashMap<Key, Node>, key: String, value: String)
match children.entry(Key::Leaf(key.clone()))
Entry::Occupied(mut state) =>
state.insert(Node::Leaf
key: key,
value: value
);
,
Entry::Vacant(state) =>
state.insert(Node::Leaf
key: key,
value: value
);
fn branch(children: &mut HashMap<Key, Node>, key: String, remainder: Vec<String>, value: String)
match children.entry(Key::Branch(key.clone()))
Entry::Occupied(mut state) =>
// We already have a branch of that name, we just
// forward the call and move on
state.get_mut().add_value(remainder, value)
,
Entry::Vacant(state) =>
// We need to create the node
let mut node = Node::Branch
key: key,
children: HashMap::new()
;
let status = node.add_value(remainder, value);
state.insert(node);
status
;
pub fn get(&self, test: &Key) -> Option<&Node>
match self
Node::Branch
key: _key,
ref children
=> children.get(test),
_ => None
pub fn add_value(&mut self, mut path: Vec<String>, value: String)
match path.len()
0 => panic!("Path cannot be empty"),
1 => Node::insert_key(contents, path.pop().unwrap(), value),
_ => Node::branch(contents, path.pop().unwrap(), path, value)
);
And finally, we create a method to construct our tree:
pub fn into_tree(collection: Vec<RawKV>) -> Node
// Create the root namespace
println!("Creating nodes");
let mut root_node = Node::Branch
key: "/".to_string(),
children: HashMap::new()
;
for node in collection
let mut path_elements:Vec<String> = node.key.split("/").map(
root_node
This is more efficient than your method in multiple respects:
- Less allocations (I am not copying nodes unless I absolutely have to)
- Less re-processing (no use of
group_by
, nopartition
, nothing but recursive tree access) - Clearer code
Let me know what you think :-)
$endgroup$
You're doing multiple clone operations because of the types you've used. Due to the fact that consul's KV store is a tree, it does not really make that much sense to represent it as a set of nested Vec
s. As I'm sure you found out, you're running into serious issues trying to figure out whether keys are inserted, what keys are inserted, and how to modify them without getting the borrow checker in a twist.
I'd like to share with you a simpler implementation, one that sidesteps all the problems you've had entirely. Since we're dealing with a KV tree, we can and should take advantage of a HashMap
for this - its type is absolutely ideal for what we are about to do. We're going to define two enum types, one for the keys, one for the value:
#[derive(Eq, PartialEq, Hash, Debug)]
pub enum Key
Leaf(String),
Branch(String)
#[derive(Eq, PartialEq, Debug)]
pub enum Node
Leaf
key: String,
value: String
,
Branch
key: String,
children: HashMap<Key, Node>
We're going to then proceed through a little game of indirection to avoid some of the reference issues we might encounter...
impl Node
fn insert_key(children: &mut HashMap<Key, Node>, key: String, value: String)
match children.entry(Key::Leaf(key.clone()))
Entry::Occupied(mut state) =>
state.insert(Node::Leaf
key: key,
value: value
);
,
Entry::Vacant(state) =>
state.insert(Node::Leaf
key: key,
value: value
);
fn branch(children: &mut HashMap<Key, Node>, key: String, remainder: Vec<String>, value: String)
match children.entry(Key::Branch(key.clone()))
Entry::Occupied(mut state) =>
// We already have a branch of that name, we just
// forward the call and move on
state.get_mut().add_value(remainder, value)
,
Entry::Vacant(state) =>
// We need to create the node
let mut node = Node::Branch
key: key,
children: HashMap::new()
;
let status = node.add_value(remainder, value);
state.insert(node);
status
;
pub fn get(&self, test: &Key) -> Option<&Node>
match self
Node::Branch
key: _key,
ref children
=> children.get(test),
_ => None
pub fn add_value(&mut self, mut path: Vec<String>, value: String)
match path.len()
0 => panic!("Path cannot be empty"),
1 => Node::insert_key(contents, path.pop().unwrap(), value),
_ => Node::branch(contents, path.pop().unwrap(), path, value)
);
And finally, we create a method to construct our tree:
pub fn into_tree(collection: Vec<RawKV>) -> Node
// Create the root namespace
println!("Creating nodes");
let mut root_node = Node::Branch
key: "/".to_string(),
children: HashMap::new()
;
for node in collection
let mut path_elements:Vec<String> = node.key.split("/").map(
root_node
This is more efficient than your method in multiple respects:
- Less allocations (I am not copying nodes unless I absolutely have to)
- Less re-processing (no use of
group_by
, nopartition
, nothing but recursive tree access) - Clearer code
Let me know what you think :-)
answered 6 hours ago
Sébastien RenauldSébastien Renauld
1312 bronze badges
1312 bronze badges
add a comment
|
add a comment
|
RedDree is a new contributor. Be nice, and check out our Code of Conduct.
RedDree is a new contributor. Be nice, and check out our Code of Conduct.
RedDree is a new contributor. Be nice, and check out our Code of Conduct.
RedDree is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f230267%2fsanitize-and-build-data-structure-from-consul-configuration%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown