diff --git a/src/ini_file.mbt b/src/ini_file.mbt index c303397..a6d4aca 100644 --- a/src/ini_file.mbt +++ b/src/ini_file.mbt @@ -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 } } @@ -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) @@ -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( @@ -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) @@ -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) @@ -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 => @@ -112,7 +113,7 @@ 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() } @@ -120,7 +121,7 @@ fn IniFile::get_section_or_create( ///| pub fn IniFile::set( self : IniFile, - section~ : String = "", + section? : String = "", key : String, value : String, ) -> Unit { @@ -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 { @@ -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() diff --git a/src/parser.mbt b/src/parser.mbt index 4e14fe1..a3a587c 100644 --- a/src/parser.mbt +++ b/src/parser.mbt @@ -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 @@ -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, @@ -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() } @@ -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 @@ -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')) @@ -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(\"\"))") } @@ -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") } ///| @@ -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") } diff --git a/src/parser_test.mbt b/src/parser_test.mbt index cf4cb54..858da8b 100644 --- a/src/parser_test.mbt +++ b/src/parser_test.mbt @@ -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. diff --git a/src/pkg.generated.mbti b/src/pkg.generated.mbti new file mode 100644 index 0000000..5d653c3 --- /dev/null +++ b/src/pkg.generated.mbti @@ -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 +