Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 19 additions & 17 deletions src/ini_file.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@ priv struct Value(String) derive(Show)

///|
struct IniFile {
sections : @hashmap.T[Section, @hashmap.T[Key, Value]]
sections : @hashmap.HashMap[Section, @hashmap.HashMap[Key, Value]]
// default not case sensitive
is_case_sensitive : Bool
}

///|
pub fn IniFile::new(is_case_sensitive~ : Bool = false) -> IniFile {
pub fn IniFile::new(is_case_sensitive? : Bool = false) -> IniFile {
IniFile::{ sections: @hashmap.new(), is_case_sensitive }
}

///|
fn IniFile::new_with_sections(
sections : @hashmap.T[Section, @hashmap.T[Key, Value]],
is_case_sensitive~ : Bool = false,
sections : @hashmap.HashMap[Section, @hashmap.HashMap[Key, Value]],
is_case_sensitive? : Bool = false,
) -> IniFile {
IniFile::{ sections, is_case_sensitive }
}
Expand All @@ -39,10 +39,11 @@ fn IniFile::lower_if_needed(self : IniFile, s : String) -> String {
}
}

///| get is a alias for get_string
///|
/// get is a alias for get_string
pub fn IniFile::get(
self : IniFile,
section~ : String = "",
section? : String = "",
key : String,
) -> String? {
self.get_string(section~, key)
Expand All @@ -51,7 +52,7 @@ pub fn IniFile::get(
///|
pub fn IniFile::get_string(
self : IniFile,
section~ : String = "",
section? : String = "",
key : String,
) -> String? {
let section_map = self.get_section(
Expand All @@ -61,10 +62,10 @@ pub fn IniFile::get_string(
},
create_if_not_exists=false,
)
guard not(section_map.is_empty()) else { None }
guard section_map is Some(_) else { None }
let section_map = section_map.unwrap()
let value = section_map.get(self.lower_if_needed(key))
guard not(value.is_empty()) else { None }
guard value is Some(_) else { None }
let value = value.unwrap()
let Value(v) = value
Some(v)
Expand All @@ -73,11 +74,11 @@ pub fn IniFile::get_string(
///|
pub fn IniFile::get_bool(
self : IniFile,
section~ : String = "",
section? : String = "",
key : String,
) -> Bool? {
let value = self.get_string(section~, key)
guard not(value.is_empty()) else { None }
guard value is Some(_) else { None }
let value = value.unwrap()
match value.to_lower() {
"true" | "yes" | "on" => Some(true)
Expand All @@ -92,8 +93,8 @@ pub fn IniFile::get_bool(
fn IniFile::get_section(
self : IniFile,
section : Section,
create_if_not_exists~ : Bool = false,
) -> @hashmap.T[Key, Value]? {
create_if_not_exists? : Bool = false,
) -> @hashmap.HashMap[Key, Value]? {
let section_map = self.sections.get(section)
match section_map {
None =>
Expand All @@ -112,15 +113,15 @@ fn IniFile::get_section(
fn IniFile::get_section_or_create(
self : IniFile,
section : Section,
) -> @hashmap.T[Key, Value] {
) -> @hashmap.HashMap[Key, Value] {
let section_map = self.get_section(section, create_if_not_exists=true)
section_map.unwrap()
}

///|
pub fn IniFile::set(
self : IniFile,
section~ : String = "",
section? : String = "",
key : String,
value : String,
) -> Unit {
Expand All @@ -133,7 +134,8 @@ pub fn IniFile::set(
section_map.set(self.lower_if_needed(key), Value(value))
}

///| Converts the IniFile to a standard INI format string
///|
/// Converts the IniFile to a standard INI format string
pub fn IniFile::to_string(self : IniFile) -> String {
fn escape_char(c : Char) -> String {
match c {
Expand Down Expand Up @@ -176,7 +178,7 @@ pub fn IniFile::to_string(self : IniFile) -> String {
let buf = StringBuilder::new()
// Global section first
let global_section = self.sections.get(Section::Global)
if not(global_section.is_empty()) {
if global_section is Some(_) {
global_section
.unwrap()
.iter()
Expand Down
24 changes: 12 additions & 12 deletions src/parser.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub suberror IniParseError {
///|
priv struct ParseContext {
mut current_section : Section
sections : @hashmap.T[Section, @hashmap.T[Key, Value]]
sections : @hashmap.HashMap[Section, @hashmap.HashMap[Key, Value]]
mut line : Int
mut col : Int
mut key : String
Expand Down Expand Up @@ -66,7 +66,7 @@ fn IniParseState::lower_if_needed(self : IniParseState, s : String) -> String {
}

///|
pub fn IniParseState::new(is_case_sensitive~ : Bool = false) -> IniParseState {
pub fn IniParseState::new(is_case_sensitive? : Bool = false) -> IniParseState {
IniParseState::{
ctx: ParseContext::{
current_section: Section::Global,
Expand All @@ -86,7 +86,7 @@ pub fn IniParseState::new(is_case_sensitive~ : Bool = false) -> IniParseState {
///|
pub fn parse(
str : String,
is_case_sensitive~ : Bool = false,
is_case_sensitive? : Bool = false,
) -> IniFile raise IniParseError {
IniParseState::new(is_case_sensitive~).parse(str).finish()
}
Expand Down Expand Up @@ -405,11 +405,11 @@ fn IniParseState::commit_value(
self : IniParseState,
// If true, drop the last space in the value
// this is used to handle the case when a comment is at the end of the line
drop_last_one_space~ : Bool = false,
drop_last_one_space? : Bool = false,
) -> IniParseState raise IniParseError {
let mut value = self.ctx.buffer.to_string()
if drop_last_one_space && value.rev_iter().nth(0).unwrap().is_whitespace() {
value = value.substring(end=value.length() - 1)
if drop_last_one_space && value is [.. rest, ch] && ch.is_whitespace() {
value = rest.to_string()
}
let section = self.ctx.sections.get(self.ctx.current_section).unwrap()
let key = self.ctx.key
Expand Down Expand Up @@ -500,7 +500,7 @@ test "IniParseState::process_char/global_section_edge_cases" {
}

///|
test "panic IniParseState::process_char/unclosed_section" {
test "panic IniParseState::process_char/unclosed_section_newline" {
let state = IniParseState::new()
ignore(state.process_char('['))
ignore(state.process_char('\n'))
Expand Down Expand Up @@ -649,7 +649,7 @@ test "IniParseState::handle_key_char/valueless_key" {
inspect(state.ctx.key, content="key")
inspect(state.phase, content="Start")
let section = state.ctx.sections.get(state.ctx.current_section)
inspect(section.is_empty(), content="false")
inspect(section is Some(_), content="true")
let value = section.unwrap().get("key")
inspect(value, content="Some(Value(\"\"))")
}
Expand Down Expand Up @@ -732,10 +732,10 @@ test "IniParseState::commit_value/basic" {
inspect(state.ctx.buffer.is_empty(), content="true")
// Verify the section exists
let section = state.ctx.sections.get(Section::NamedSection("section"))
inspect(section.is_empty(), content="false")
inspect(section is Some(_), content="true")
// Verify the key exists in the section
let value = section.unwrap().get("key")
inspect(value.is_empty(), content="false")
inspect(value is Some(_), content="true")
}

///|
Expand All @@ -752,8 +752,8 @@ test "IniParseState::commit_value/empty_value" {
let state = state.commit_value()
// Verify the section exists
let section = state.ctx.sections.get(Section::NamedSection("section"))
inspect(section.is_empty(), content="false")
inspect(section is Some(_), content="true")
// Verify the key exists in the section with empty value
let value = section.unwrap().get("key")
inspect(value.is_empty(), content="false")
inspect(value is Some(_), content="true")
}
7 changes: 4 additions & 3 deletions src/parser_test.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,10 @@ test "parse/error_value_without_section_or_key" {
// This case is tricky because the parser might interpret 'value' as a key
// Let's test assignment without a preceding key
let input = "[section]\n=value"
match (try? parse(input)) {
Ok(_) => assert_true(false)
Err(_) => assert_true(true)
try parse(input) catch {
_ => assert_true(true)
} noraise {
_ => assert_true(false)
}
// The ValueWithoutSection error is harder to trigger directly with current logic,
// as '=' is required to start reading a value, which implies a key was being read.
Expand Down
34 changes: 34 additions & 0 deletions src/pkg.generated.mbti
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Generated using `moon info`, DON'T EDIT IT
package "ShellWen/sw_ini"

// Values
pub fn parse(String, is_case_sensitive? : Bool) -> IniFile raise IniParseError

// Errors
pub suberror IniParseError {
UnexpectedEqualSign(line~ : Int, col~ : Int)
EmptySection(line~ : Int, col~ : Int)
UnclosedSection(line~ : Int, col~ : Int)
ValueWithoutSection(line~ : Int, col~ : Int)
QuoteMismatch(line~ : Int, col~ : Int)
QuoteNotClosed(line~ : Int, col~ : Int)
}

// Types and methods
type IniFile
pub fn IniFile::get(Self, section? : String, String) -> String?
pub fn IniFile::get_bool(Self, section? : String, String) -> Bool?
pub fn IniFile::get_string(Self, section? : String, String) -> String?
pub fn IniFile::new(is_case_sensitive? : Bool) -> Self
pub fn IniFile::set(Self, section? : String, String, String) -> Unit
pub fn IniFile::to_string(Self) -> String

type IniParseState
pub fn IniParseState::finish(Self) -> IniFile raise IniParseError
pub fn IniParseState::new(is_case_sensitive? : Bool) -> Self
pub fn IniParseState::parse(Self, String) -> Self raise IniParseError

// Type aliases

// Traits